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.
Thanks for the tutorial on Django contact form. I’m learning Python and using Visual Studio Community 2015 and it was very helpful!
Thanks a lot. This is only tutor i found and it works great!
Glad that it helped someone.
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’),
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)
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?
where is file forms.py?
You have to create this yourself
in what place form.py must be?
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.