Build a Django Contact Form with Email Backend

April 17, 2020, 12:29 p.m.
Django · 6 min read
Build a Django Contact Form with Email Backend
Last Modified: Sept. 8, 2020, 4:03 p.m.

Many websites list their contact email address in the footer where it is up to the user to send an email.  While this is by no means a bad way for users to contact staff, it requires more work and effort on the user's side than what's necessary. 

So let's help the user easily send a contact email with an in-site contact form that instantly sends an email to the website staff upon submission.  While this may seem daunting at first, Django actually handles most of the heavy lifting. 

We will cover (1) how to create the Django contact form fields, (2) how to add the form to the HTML template, and (3) how to add a Django email backend.

 

Create a Django contact form in forms.py

env > mysite > main > forms.py

from django import forms

# Create your forms here.

class ContactForm(forms.Form):
	first_name = forms.CharField(max_length = 50)
	last_name = forms.CharField(max_length = 50)
	email_address = forms.EmailField(max_length = 150)
	message = forms.CharField(widget = forms.Textarea, max_length = 2000)

To make the contact form we need to edit forms.py. The form will be called ContactForm and require basic contact information prior to submission. Django does not come with forms.py created, so if you do not have a forms.py file, create one in the mysite > main folder.

Add first_name and last_name as Django CharFields. These are character fields that allow a maximum of 50 characters each. Then add a new field called email_address as an EmailField, a specific field that checks for an @ address in the field input before form submission. Finally add the message field as a CharField with a widget specifying the form will display as a Textarea, a form field that accepts paragraph styled text.

Save the changes to this file.

 

Update the function in views.py

env > mysite > main > views.py

from django.shortcuts import render, redirect
from .forms import ContactForm
from django.core.mail import send_mail, BadHeaderError
from django.http import HttpResponse


# Create your views here.
def homepage(request):
	return render(request, "main/home.html")

def contact(request):
	if request.method == 'POST':
		form = ContactForm(request.POST)
		if form.is_valid():
			subject = "Website Inquiry" 
			body = {
			'first_name': form.cleaned_data['first_name'], 
			'last_name': form.cleaned_data['last_name'], 
			'email': form.cleaned_data['email_address'], 
			'message':form.cleaned_data['message'], 
			}
			message = "\n".join(body.values())

			try:
				send_mail(subject, message, 'admin@example.com', ['admin@example.com']) 
			except BadHeaderError:
				return HttpResponse('Invalid header found.')
			return redirect ("main:homepage")
      
	form = ContactForm()
	return render(request, "main/contact.html", {'form':form})

Open views.py and import the ContactForm from forms.py along with send_mail, BadHeaderError, HttpResponse, and redirect from Django. Then add an if statement that if the user requests to post the form, check to see if the form is valid. If the form is valid, then declare the subject and body of the email.

The subject will be "Website Inquiry" while the body will be all of the cleaned form fields. Django has built-in form validation that cleans the data for you, all you need to do is call the form.cleaned_data['form_field']. To format the actual email message, join all of the body's values together.

With this completed, the function will try to send the email with the subject, message, from email, and email declared. Django requires the format  send_mail to be ('subject', 'message', 'from_email', ['to_email']).

The from and to email addresses are both set to admin@example.com so the email will appear in the terminal for testing purposes. 

Now, to prevent attackers from inserting extra email headers, we need to return a bad header error. If a bad header is added, let's return a HttpResponse that the header is invalid. Else, if the form is submitted correctly, the user will be redirected to the homepage.

If you don't have a homepage view follow the instructions on The Beginners Guide to Django

Finally, if the user simply requests to see the page, the empty form displays in the contact.html template.

 

Add a contact URL path

env > mysite > main > urls.py

from django.urls import path
from . import views

app_name = "main"

urlpatterns = [
    path("", views.homepage, name="homepage"),
    path("contact", views.contact, name="contact"),
    
]

Now let's add the contact page URL to our app's urls.py. If you are using an existing template with a URL path already listed, skip this step. 

 

Add the form to contact.html

env > mysite > main > templates > main > contact.html

	<!--Contact form-->
	<div style="margin:80px">
		<h1>Contact</h1>
		<h4>Contact us directly if you have any questions</h4>
		<p>
			Please write your name, email address and a message below if you have any questions.
			One of our staff members will be happy to contact you directly and answer your questions as soon as possible. 
		</p>
		<form method="post">
        {% csrf_token %}
            {{form.as_p}}
            <button type="submit">Submit</button>
        </form>
    </div>

Open contact.html, or the template file of your choice, and add a form HTML element below the text.  Add method="POST" to the element so the form is posted upon submission. Within the form element, nest the {% csrf_token %} to prevent form submission forgery. This is a very important line of code that allows your Django project to securely send information.

Next, call the ContactForm fields after the CSRF token using the template context; use as_p to wrap the fields in <p> tags. You can also choose to call fields individually using the format {{form.name_of_field}}.

Lastly, add a submit button right before the closing tag of the form element. 

If you are using the CSS framework Bootstrap, learn how to render your Django form with Bootstrap.

 

Add the Django email backend to the settings

env > mysite > main > settings.py

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

For developer mode, send the email to the CLI and not to an email inbox.

In settings.py add the following line of code at the bottom of the page. This line sets the Django email backend as the Command Prompt/Terminal for testing purposes but will eventually be changed to an actual email sending service for production. 

 

Send a test email to the CLI

macOS Terminal/Windows Command Prompt

__________________________________________________________________________________________
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: Website Inquiry
From: admin@example.com
To: admin@example.com
Date: Wed, 05 Feb 2020 00:04:43 -0000
Message-ID: [123456789@DESKTOP]

John
Smith
john@gmail.com
Hello, I have a question...

__________________________________________________________________________________________

Now you can submit your contact form in the browser by going to http://127.0.0.1:8000/contact. Upon submission, you will be instantly redirected to the homepage.

But how do you know it worked correctly?

Go to your Command Prompt/Terminal and there should be an output similar to the one above given that we set the Django email backend to output in the console rather than actually sending an email.

 

Sending emails in production

When you are ready for production, learn how to configure your Django contact to send to an actual email address using the AWS SES Email Backend.

 

Django 'django.core.mail.backends.console.EmailBackend' not working

If the Django email backend in the settings is not working:

1. Check to make sure there are no spelling errors. 

2. Check to make sure the EMAIL_BACKEND has quotation marks.

env > mysite > main > settings.py

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

3. If those are not the cause of the error, go to the views.py file and make sure send_email and BadHeaderError are imported from django.core.mail and HttpResponse is imported from django.http at the top of the file.

env > mysite > main > views.py

from django.shortcuts import render, redirect
from .forms import ContactForm
from django.core.mail import send_mail, BadHeaderError
from django.http import HttpResponse


# Create your views here.
def contact(request):
	...

4. Finally, make sure you include try and except in the views function or the function will not work properly and the email will not send.

env > mysite > main > views.py

from django.shortcuts import render, redirect
from .forms import ContactForm
from django.core.mail import send_mail, BadHeaderError
from django.http import HttpResponse


# Create your views here.
def contact(request):
	if request.method == 'POST':
		form = ContactForm(request.POST)
		if form.is_valid():
			subject = "Website Inquiry" 
		    body = {
			'first_name': form.cleaned_data['first_name'], 
			'last_name': form.cleaned_data['last_name'], 
			'email': form.cleaned_data['email_address'], 
			'message':form.cleaned_data['message'], 
			}
			message = "\n".join(body.values())

			try:
				send_mail(subject, message, 'admin@example.com', ['admin@example.com']) 
			except BadHeaderError:
				return HttpResponse('Invalid header found.')
			return redirect ("main:homepage")
      
	form = ContactForm()
	return render(request, "main/contact.html", {'form':form})

 

 






Post a Comment
Join the community

9 Comments
Ramesh Jan. 8, 2021, 11:15 p.m.

How to send this same information to actual Gmail instead of sending on console... Please help me out.. Thank you

Jaysha replying to Ramesh Jan. 20, 2021, 10:17 a.m.

To send emails from within your application, you need to sign up for an email service such as Amazon SES or SendGrid. There is a link to the Amazon SES Email Backend tutorial above.

Jaysha replying to Ramesh Jan. 20, 2021, 10:22 a.m.

We recommend Amazon SES because it's a pay-as-you-go service where you can send 62,000 messages per month at no charge, all on the free-tier.

Bryan March 25, 2021, 5 p.m.

Hey, I'm stuck on testing this on the Command Line. It is not working for me, and I'm getting no errors. But I see in you say to make sure 'send_email' is imported but all your examples have 'send_mail', can you clarify which one is it?

Jaysha replying to Bryan March 26, 2021, 10:06 a.m.

Double-check you're not getting a terminal error or Django yellow page. Terminal errors are easy to miss.

Jaysha replying to Bryan March 26, 2021, 10:07 a.m.

My best guess is you copied and pasted the entire views.py directly to your project and have the error "IndentationError: unindent does not match any outer indentation level" with a trackback pointing to body = { in the views.py.

Jaysha replying to Jaysha March 26, 2021, 10:12 a.m.

I updated views.py so copying and pasting no longer causes the indentation error. send_mail is imported at the top of views.py. You get a Django yellow page "name 'send_mail' is not defined" if you forgot it. Hope that helps!

Bryan replying to Jaysha April 1, 2021, 9:49 p.m.

Hey, I figured it out it wasn't an indentation error. I'm not too sure what the exact problem was, but I started used an actual email server instead of sending it to my console and a different views setup and it worked. Thanks for the tutorial thoughh

Jaysha replying to Bryan April 5, 2021, 2:26 p.m.

Happy you solved your problem! Good luck with your project!

2
Jaysha
Written By
Jaysha
Hello! I enjoy learning about new CSS frameworks, animation libraries, and SEO.