django-contact-form Full Tutorial / Custom Example in Django 1.7

If you are just starting out with Django you might be struggling with adding 3rd Party Apps due to the lack of documentaton. django-contact-form has at least some documentation. At least I didn’t quite knew how to customize the contact form as it doesn’t come with any templates. I will show you all the basic templates and a customized contact form.

Here is a full example.
In urls.py either use this

(r'^contact/', include('contact_form.urls')),

or

url(r'contact/', CustomContactFormView.as_view(), name='contact_form'),

The later refers to this class in views.py:

class CustomContactFormView(FormView):
    form_class = CustomContactForm
    template_name = 'contact_form/contact_form.html'

    def form_valid(self, form):
        form.save()
        return super(CustomContactFormView, self).form_valid(form)

    def get_form_kwargs(self):
        # ContactForm instances require instantiation with an
        # HttpRequest.
        kwargs = super(CustomContactFormView, self).get_form_kwargs()
        kwargs.update({'request': self.request})
        return kwargs

    def get_success_url(self):
        # This is in a method instead of the success_url attribute
        # because doing it as an attribute would involve a
        # module-level call to reverse(), creating a circular
        # dependency between the URLConf (which imports this module)
        # and this module (which would need to access the URLConf to
        # make the reverse() call).
        return reverse('contact_form_sent')

Note that I still return to the original contact_form_sent url as defined in contact_form.urls. So adapt this!

Now on to the templates, adapt name and path.

templates/contact_form/contact_form.html:

{% extends 'base.html' %}

{% block body_block %}
                        <form method="post">{% csrf_token %}
                            {% for field in form %}
                                <div class="fieldWrapper">
                                    {{ field.errors }}
                                    {{ field.label_tag }} {{ field }}
                                </div>
                            {% endfor %}
                            <input type="submit" value="Submit">
                        </form>
{% endblock %}

templates/contact_form/contact_form.txt (the mail to be send):

From: {{ name }} {{ email }}
Message: {{ reason }}
{{ body }}

You can put anything in the mail, however those tags need to be defined in your form class. More on that further down below.

templates/contact_form_contact_form_sent.html:

{% extends 'base.html' %}

{% block body_block %}
We received your message and will get back to you.
{% endblock %}

templates/contact_form/contact_form_subject.txt (subject of the mail):

Contact Form Submitted

And now the most important part, forms.py:

class CustomContactForm(ContactForm):
    def __init__(self, request, *args, **kwargs):
        super(CustomContactForm, self).__init__(request=request, *args, **kwargs)
        fields_keyOrder = ['reason', 'name', 'email', 'body', 'captcha']
        if (self.fields.has_key('keyOrder')):
            self.fields.keyOrder = fields_keyOrder
        else:
            self.fields = OrderedDict((k, self.fields[k]) for k in fields_keyOrder)

    REASON = (
        ('support', 'Support'),
        ('feedback', 'Feedback'),
        ('delete', 'Account deletion')
    )
    reason = forms.ChoiceField(choices=REASON, label='Reason')
    captcha = CaptchaField()
    template_name = 'contact_form/contact_form.txt'
    subject_template_name = "contact_form/contact_form_subject.txt"

This strange looking init method is needed to get a custom order of the fields working with Django 1.7!

In the fields_keyOrder List you can specify the order the fields should be rendered in your template. Be sure to not forget any field, or it won’t be rendered. Before Django 1.7 there was a simple one-line solution but that didn’t work for me.
Note that I also installed the captcha addon which works just fine with your custom contact form.

Aside from that all that is left, is the configuration in your settings.py:

# Contact Form specific
EMAIL_USE_TLS = True
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'tobias@atsoftware'  # this is my email address, use yours
EMAIL_HOST_PASSWORD = os.environ['EMAIL_HOST_PASSWORD']   # set environ yourself
DEFAULT_FROM_EMAIL = 'webmaster@localhost'

If you set the backend to:

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

it will print out the mail to the console when you run Django on your development machine (with manage.py runserver)

I hope that someone will find this post helpful. I think every 3rd Party App should come with basic templates to get you started. Still it’s a nice addon that can save you a little bit of time.

16 thoughts on “django-contact-form Full Tutorial / Custom Example in Django 1.7

      • One more question to you. I have error – “NoReverseMatch at /contact-admin/ Reverse for ‘contact_form_sent’ with arguments” when try to submit the form. What i missed? Is there some more modification exept of this “(r’^contact/’, include(‘contact_form.urls’))” in url.py?

        • Resolved, I gave a name to url “name=contact-admin” but tried to redirect in def get-succes_url to “contact_admin”.

          • And the last question 🙂 I try redirect after submit the form to contact_form_sent.html and I made such urls
            url(r’^contact-admin/’, CustomContactFormView.as_view(), name=’contact_form’),
            url(r’^contact-form-sent/’, CustomContactFormView.as_view(), name=’contact_form_sent’)

            but when I submit I I am back to the form page. I see that this “CustomContactFormView.as_view()” must be wrong in the second url. But how to redirect to contact_form_sent in other way?

          • You don’t want to have a second CustomContactFormView.as_view() – that doesn’t really make sense.
            If you just delete this line it should already take you to the default url, so just create a template in templates/contact_form/contact_form_sent.html – that should be it. So it will work if you use the default return reverse(‘contact_form_sent’), then it should already be defined in contact_form.urls.

            You can of course overwrite this and do something like this in your urls.py:
            url(r’^report/sent/$’, TemplateView.as_view(template_name=’contact_form/report_form_sent.html’), name=’report_form_sent’),

  1. Works only this variant url(‘^contact-admin/’, include(‘contact_form.urls’)) But I cann`t apply to this crispy-forms style. Did you try it?

    • Sorry, I haven’t used Crispy Forms. Looked at in it once but didn’t use it in the end. If you are really stuck, I’d recommend going to your site-packages folder, copy the contact_form folder to your project, have a look at the source code and change it if needed. That’s probably not the best way, but it helped me to make changes to other unmaintained modules. Or you might have to look at stackoverflow for help with Crispy Forms and django-contact-forms.

      • It works. but I used this kind of url url(r’^contact-admin/’, CustomContactFormView.as_view(), name=’contact_form’), redirected to the home page, where I`ve used “status message” to show succeslul message state:
        def get_success_url(self):
        message = “Message sent successfully”
        return u’%s?status_message=%s’ %(reverse(‘home’), message)

  2. And more question:
    def form_valid(self, form):
    form.save()
    return super(CustomContactFormView, self).form_valid(form)
    Is this “form.save()” – the send mail attribute which is talked about in official guide ?
    “To send the message, call the form’s save method, which accepts the keyword argument fail_silently and defaults it to False. This argument is passed directly to send_mail, and allows you to suppress or raise exceptions as needed for debugging. The save method has no return value.”
    Or there should one other def save() method?

  3. How can I add this contact form to my main page? I’m able to create a contact form and send emails on another page like you showed (e.g., myportfolio.com/contact), but I want the contact form to show up on my main page (e.g., myportfolio.com). Unfortunately, I’m not able to do this. I guess the form needs to be register somehow like it happens when visiting the /contact page, but if I embed the code into my base.html nothing shows up. This makes me think there’s some missing connection.

    • EDIT: For some reason, HTML Code is not escaped, and in general Code quotes are messed up, I am sorry for that! I removed the HTML tags <>

      Hi Grant,

      it certainly is possible to use the form on any page. What you have to do, is to add it to the context dict in your view like this:

      report_form = ReportForm(request=request)
      context_dict = {'report_form': report_form}

      In my forms.py I have a class inheriting from Contactform, e.g class ReportForm(ContactForm)
      Now my html template code is quite complicated because we use a modal view and bootstrap to display the form when a button is clicked, but essentially you want to do some like this (this should be HTML Code, but I had to remove all <> from it – I am too busy to deal with WP functions right now, you should hopefully be able to parse and understand the html even without it, the important part is the report_form):


      form id="reportForm" class="form-horizontal" method="post"
      {% csrf_token %}
      {% bootstrap_form report_form layout='horizontal' %}
      /form
      button type="button" class="btn btn-default" data-dismiss="modal" onclick="cancelReport()" Cancel
      /button
      button id="sendReportButton" type="button" class="btn btn-primary" Send /button

      Ignore the boilerplate code. We execute sending the form using Javascript


      $(document).on('click', '#sendReportButton', function () {
      $('#reportModal').modal('hide');

      $.ajax({
      type: 'POST',
      url: '/play/report/',
      data: $('#reportForm').serialize(),
      success: function (response) {
      alert("Report sent.");
      },
      error: function (xhr, err) {
      alert("There was an error sending the report: " + xhr.responseText);
      }
      });
      });

      What happens here is basically a request to a certain view /play/report, which looks like this:


      def report(request):
      form = ReportForm(request=request, data=request.POST)

      if form.is_valid():
      form.save()
      # do more stuff, e.g get stuff from POST with request.POST['someTag']
      return HttpResponse()
      else:
      return HttpResponseBadRequest()

      Hope this helps to bring you on the right track.

Leave a Reply

Your email address will not be published. Required fields are marked *