Building Contact Us Form
Last updated on July 27, 2020
In this lesson, we are going to create a Contact form. This will allow our visitors to send feedback directly to the admin email address.
The forms we have built in the last few chapters were closely related to the models class. However, It is also possible (and sometimes more feasible) to create forms that stand on its own without having any relationship with the model. To give you an example of the complete process, the Contact form we will build in this chapter will inherit from forms.Form
class instead of forms.ModelForm
. But before we do that, we have to learn how to send emails with Django.
Sending Email with Django #
To send email, Django requires you to add some configurations. The following is a list of some common configuration options provided by Django:
SERVER_EMAIL
: It specifies the email address which Django will use to send error messages toADMINS
andMANAGERS
.EMAIL_BACKEND
: It specifies the name of the backend to use for sending emails. Thedjango.core.mail.backends.smtp.EmailBackend
means that Django will use SMTP Server to send emails. Django has many other backends. Here are other two commonly used backends:django.core.mail.backends.filebased.EmailBackend
django.core.mail.backends.console.EmailBackend
The former allows us to write the email to a file instead of forwarding it to an SMTP server. And the latter prints the email directly to the console.
EMAIL_HOST
: It specifies the address of the Email Server or SMTP Server.EMAIL_HOST_USER
: It specifies the username of the SMTP Server.EMAIL_HOST_PASSWORD
: Password of the SMTP server.EMAIL_PORT
: Port to use to connect to the SMTP server.EMAIL_USE_TLS
: It specifies whether to use TLS secure or not.DEFAULT_FROM_EMAIL
: It specifies the default email address to use for ordinary correspondence from the site managers.ADMINS
: It specifies a list of people to send error notifications. When the site is in production (i.eDEBUG = False
) and any view raises an exception then Django will send an email to all the to all the people specified in theADMINS
list. Each item inADMINS
list is a tuple. For example:1 2 3 4
ADMINS = [ ('name1', 'name1@email.com'), ('name2', 'name2@email.com'), ]
MANAGERS
: It specifies a list of people to send broken link emails for 404 NOT FOUND errors. It accepts emails in the same format asADMINS
.1 2 3 4
MANAGERS = [ ('name1', 'name1@email.com'), ('name2', 'name2@email.com'), ]
To enable this feature you'll have add
django.middleware.common.BrokenLinkEmailsMiddleware
middleware in theMIDDLEWARE
setting insettings.py
file.
The following listing shows configurations required to send emails via Gmail SMTP server.
1 2 3 4 5 6 7 8 | SERVER_EMAIL = 'infooveriq@gmail.com'
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_PASSWORD = 'password'
EMAIL_HOST_USER = SERVER_EMAIL
EMAIL_PORT = 587
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
|
We are in the development phase and just want to send test emails. As a result, we will use the console backend. Open settings.py
and add the following options at end of the file:
djangobin/django_project/django_project/settings.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #...
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
SERVER_EMAIL = 'infooveriq@gmail.com'
DEFAULT_FROM_EMAIL = SERVER_EMAIL
ADMINS = (
('OverIQ', 'admin@overiq.com'),
)
MANAGERS = (
('OverIQ', 'manager@overiq.com'),
)
|
Testing email using sendtestemail #
Django provides sendtestemail
command which sends a test email to the specified email id. It uses email id specified in DEFAULT_FROM_EMAIL
setting to send the email.
In the terminal enter the following command:
1 2 3 4 5 6 7 8 9 10 11 12 | $ ./manage.py sendtestemail test@example.com
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: Test email from pc on 2018-04-14 08:02:32.394195+00:00
From: infooveriq@gmail.com
To: test@example.com
Date: Sat, 14 Apr 2018 08:02:32 -0000
Message-ID: <20180414080232.16065.81775@pc>
If you're reading this, it was successful.
-------------------------------------------------------------------------------
|
If you get the same output as above, then it means everything is working as expected.
Instead of sending email to the specified email id, you can also have sendtestemail
to send emails to ADMINS
or MANAGERS
by using --admins
or --managers
options:
1 2 3 4 5 6 7 8 9 10 11 12 | $ ./manage.py sendtestemail --managers
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [Django] Test email from pc on 2018-04-14 08:05:12.545568+00:00
From: infooveriq@gmail.com
To: manager@overiq.com
Date: Sat, 14 Apr 2018 08:05:12 -0000
Message-ID: <20180414080512.16961.91759@pc>
This email was sent to the site managers.
-------------------------------------------------------------------------------
|
1 2 3 4 5 6 7 8 9 10 11 12 | $ ./manage.py sendtestemail --admins
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [Django] Test email from pc on 2018-04-14 08:08:31.878881+00:00
From: infooveriq@gmail.com
To: admin@overiq.com
Date: Sat, 14 Apr 2018 08:08:31 -0000
Message-ID: <20180414080831.17122.89062@pc>
This email was sent to the site admins.
-------------------------------------------------------------------------------
|
send_mail() function #
The send_mail()
is the easiest way to send emails in Django. It's syntax is:
send_mail(subject, message, from_email, recipient_list)
Subject
: Subject of the email.
message
: Message.
from_email
: Sender's email
recipient_list
: A list of recipients
On success send_mail()
returns 1
, otherewise 0
.
mail_admins() function #
The main_admins()
function sends email to the administrators specified in the ADMINS
setting. It's syntax is:
mail_admins(subject, message, fail_silently=False)
Subject
: the subject of the email.
message
: message to send.
It uses email specified in the SERVER_EMAIL
setting as the sender's email.
Since we are interested in sending email to the admins, this is the function we are going to use while building the Contact Us page.
Creating Contact Us Page #
Open forms.py
file and add ContactForm
class to wards the end of the file as follows:
djangobin/django_project/djangobin/forms.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #...
class ContactForm(forms.Form):
BUG = 'b'
FEEDBACK = 'fb'
NEW_FEATURE = 'nf'
OTHER = 'o'
purpose_choices = (
(FEEDBACK, 'Feedback'),
(NEW_FEATURE, 'Feature Request'),
(BUG, 'Bug'),
(OTHER, 'Other'),
)
name = forms.CharField()
email = forms.EmailField()
purpose = forms.ChoiceField(choices=purpose_choices)
message = forms.CharField(widget=forms.Textarea(attrs={'cols': 40, 'rows': 5}))
|
In the views.py
file, add a view named contact
as follows:
djangobin/django_project/djangobin/views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #...
from django.core.mail import mail_admins
import datetime
from .forms import SnippetForm, ContactForm
from .models import Language, Snippet, Tag
from .utils import paginate_result
#...
def trending_snippets(request, language_slug=''):
#...
def contact(request):
if request.method == 'POST':
f = ContactForm(request.POST)
if f.is_valid():
name = f.cleaned_data['name']
subject = "You have a new Feedback from {}:<{}>".format(name, f.cleaned_data['email'])
message = "Purpose: {}\n\nDate: {}\n\nMessage:\n\n {}".format(
dict(f.purpose_choices).get(f.cleaned_data['purpose']),
datetime.datetime.now(),
f.cleaned_data['message']
)
mail_admins(subject, message)
messages.add_message(request, messages.INFO, 'Thanks for submitting your feedback.')
return redirect('djangobin:contact')
else:
f = ContactForm()
return render(request, 'djangobin/contact.html', {'form': f})
|
This view shows the Contact form and sends the submitted response to all the admins.
Notice how we are setting the purpose in the in the email body. The data of the purpose
field will be the first element of the tuple in purpose_choices
, that is b
, fb
, nf
or o
. These letters are not very helpful. That's why we first convert the tuple into a dictionary and then use the key stored in cleaned_data
to access the more readable value.
Next, create a template named contact.html
in the templates
directory with the following code:
djangobin/django_project/djangobin/templates/djangobin/contact.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | {% extends 'djangobin/base.html' %}
{% block title %}
Contact Us - {{ block.super }}
{% endblock %}
{% block main %}
<h4>Contact </h4>
<hr>
{% if messages %}
{% for message in messages %}
<p class="alert alert-info">
{{ message }}
</p>
{% endfor %}
{% endif %}
<form method="post">
{% csrf_token %}
<div class="form-group row">
<div class="col-lg-5">
{{ form.name.errors }}
{{ form.name.label_tag }}
{{ form.name }}
</div>
</div>
<div class="form-group row">
<div class="col-lg-5">
{{ form.email.errors }}
{{ form.email.label_tag }}
{{ form.email }}
</div>
</div>
<div class="form-group row">
<div class="col-lg-5">
{{ form.purpose.errors }}
{{ form.purpose.label_tag }}
{{ form.purpose }}
</div>
</div>
<div class="form-group row">
<div class="col-lg-5">
{{ form.message.errors }}
{{ form.message.label_tag }}
{{ form.message }}
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
{% endblock %}
|
Add a new URL pattern in urls.py
as follows:
djangobin/django_project/djangobin/urls.py
1 2 3 4 5 6 7 8 | #...
urlpatterns = [
#...
url('^tag/(?P<tag>[\w-]+)/$', views.tag_list, name='tag_list'),
url('^download/(?P<snippet_slug>[\d]+)/$', views.download_snippet, name='download_snippet'),
url('^raw/(?P<snippet_slug>[\d]+)/$', views.raw_snippet, name='raw_snippet'),
url('^contact/$', views.contact, name='contact')
]
|
Next, add a link to Contact us form in the base.html
as follows:
djangobin/django_project/djangobin/templates/djangobin/base.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | {# ... #}
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li {% if request.path == '/' %}class='active'{% endif %} >
<a href="{% url 'djangobin:index' %}">Add new</a>
</li>
<li {% if request.path == '/trending/' %}class='active'{% endif %}>
<a href="{% url 'djangobin:trending_snippets' %}">Trending<span class="sr-only">(current)</span></a>
</li>
<li {% if request.path == '/about/' %}class='active'{% endif %}>
<a href="">About</a>
</li>
<li {% if request.path == '/contact/' %}class='active'{% endif %}>
<a href="{% url 'djangobin:contact' %}">Contact</a>
</li>
</ul>
{# ... #}
|
To view our creation visit http://127.0.0.1:8000/contact/
and try submitting a feedback or two.
After submission you will get a success message like this:
In the shell running the server you should get output as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | [10/May/2018 14:19:05] "GET /contact/ HTTP/1.1" 200 9875
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [Django] You have a new Feedback from spike:<spike@mail.com>
From: infooveriq@gmail.com
To: admin@overiq.com
Date: Thu, 10 May 2018 14:19:30 -0000
Message-ID: <20180510141930.6825.79585@pc>
Purpose: Feedback
Date: 2018-05-10 14:19:30.825265
Message:
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Amet, animi assumenda eos exercitationem incidunt molestias nihil non quas soluta voluptatibus! Architecto blanditiis eos nulla quas! A odit optio quibusdam voluptatibus!
-------------------------------------------------------------------------------
|
Load Comments