Beginner Lesson 8: A Simple Contact Form

Transcript

Transcript References
The video mentions copying code from the screen or transcript. This is a reference to the old, paid version of the course. I have now collated all the code together in one place in the source, which you can download here.

Forms Overview

HTML forms are a core component of modern websites. From Google’s simple search box to large, multi-page submissions, HTML forms are the primary means of collecting information from website visitors and users.
The code for a basic HTML form is quite simple. Let’s practice by creating a form that collects a user’s first and last name. Pause the video and enter the code into your editor, or copy from your transcript:

The form HTML tags define the form element and each of the form fields are contained within the form element. In this form, I have defined two text fields and a submit button. In HTML5, there are many other field element types including email fields, date and time fields, checkboxes, radio buttons and more.

You can see that I have rendered the form elements as paragraphs in this example. It’s also very common to render forms as an ordered or unordered list or as a table with the fields filling out the rows of the table.

If you open the file in your web browser, it should look like this.

I’m opening the file directly from inside Visual Studio Code, but you can just as easily open it with Windows explorer as html files should automatically open with your default browser.

While creating a basic form is simple, things get much more complicated once you need to use the form in a real-life situation. In an actual website, you must validate the data submitted with the form. If the field is required, you must check that the field isn’t blank. If the field isn’t blank, you then need to check that the data submitted is the valid data type. For example, if you are requesting an email address, you must check that a valid email address is entered.

You must also ensure that your form deals with entered data in a safe way. A common way that hackers target a website is to submit malicious program code via forms to try and hack into the site.

To complicate matters further, website users expect feedback when they haven’t filled out a form correctly. So, you must also have some way of displaying errors on the form for the user to correct before allowing them to submit the form.

Creating forms, validating data and providing feedback is a tedious process if you code it all by hand. Django is very flexible in its approach to form creation and management. If you really want to design your forms from scratch like this, Django doesn’t do a lot to get in your way.

However, I don’t recommend doing this. Unless you have a very special application in mind, Django has many tools and libraries that make form building much simpler. In particular, Django’s form class offers a very convenient set of class methods that will take care of most of the form processing and validation for you.

With a Form class, you create a special class that looks a lot like a Django model. Form class fields have built-in validation, depending on the field type, as well as an associated HTML widget.

Let’s explore the Form class a bit further with the Django interactive shell. Open the shell from within your virtual environment with python manage.py shell.

Once the shell is running, follow along with me to create a new form class. Remember if I am going a bit fast for you, just pause the video until you catch up.

First, to use the Form class, we need to import the forms module from Django.

Then we create our new Form class, which inherits from Django’s forms.Form class. We’re going to call our class SimpleForm.

Our SimpleForm class has two text fields – firstname and lastname – just like our simple HTML form.

Notice that the field declarations are almost identical to Django’s model field declarations. This is the first big plus for Django’s Form class—you don’t have to remember a new syntax for declaring form fields. But it gets better.

Let’s create an instance of the SimpleForm class and name it f.

Now you have an instance of the form class, you have some really useful class methods at your disposal. as_p() is a class method that formats the form as paragraphs. You can see by the output that Django has created your form elements for you without you having to write a single HTML tag!

Django doesn’t just output HTML paragraphs—you can also get Django to output HTML for displaying your form as a list or a table. Let’s try out the as_ul() and as_table() methods.

You will notice that Django doesn’t generate the form elements or the submit button for you. Nor does it generate the ul or table elements. This is because they are structural elements on your page, so should remain in the template.

Django’s Form class also handles validation for you. Let’s try this out in the shell:

First, let’s create a new instance of the SimpleForm class and pass an empty dictionary ({}) to the form.

When Django created the Form class, it made firstname and lastname required by default, so when we run the is_valid() method on the empty form, it returns False.


Finally, if form validation does fail, Django will create a dictionary of error messages. We can access this dictionary via the errors attribute of the Form class.

One other time-saving feature of the Form class is, when a form doesn’t validate, Django re-renders the form with the error messages added automatically. Let’s print our invalid form out in the shell:

You can see that the errors have been added to the form for you as unordered lists.
Let’s copy this automatically generated code into the simple HTML form we create earlier.

When you open this form in your browser, it will look something like this. The fact that Django does all of this automatically for you with the Form class can save you a huge amount of time – especially on more complex forms than our simple user name form!

Now that we’ve had a good look at how Django’s Form class works, let’s create our first form for the website. We are going to start with a simple form that is common to most websites—a contact form.

Creating the Contact Form

Form classes live in a file called forms.py. As not all apps will need forms, Django doesn’t automatically create this file for you so, to create our ContactForm class, we first need to create the forms.py file.
You can create this file in your site project, but because the contact form is a page on our site, it’s more logical to me to create it inside the pages app. Pause the video and enter the code into your editor, or copy from your transcript:

This is like the SimpleForm class we were playing with in the shell, with some differences:
In line 4 we set the label attribute for the yourname field to “Your Name”. If you don’t specify the label attribute, Django uses the field name for the field label by default, which in this case is not very human-friendly.

In line 5, we don’t want the email address to be a required field, so we set the required attribute to False so the person submitting the form can leave the email field blank. We’re also changing the default label of the email field to “Your e-mail address” to make it more reader-friendly.

And in line 7, the message field must allow the person submitting the form to enter a detailed message, so we are setting the field widget to a Textarea, replacing the default TextInput widget.

Now that we’ve created our ContactForm class, we have a few tasks to complete to get it to render on our website.

  1. Add our form to the list of URLs in our pages app;
  2. Add navigation to our site template;
  3. Create a template for the contact form; and
  4. Create and test a new view to manage the contact form.

Add URL to Pages App

To show our contact form, first we create a URL for it. To do that, we need to modify our app’s urls.py file. Pause the video and enter the code into your editor, or copy from your transcript:

In line 7 we’ve added a URL pattern that will direct the URL ending in “contact” to the new contact view we’ll write shortly. Make sure the new URL pattern is before the index view in the list. If you put it after the index view pattern, Django will throw an exception because the pagename capturing group will match the contact URL and load the index view instead of the contact view.

Add Navigation to Site Template

The most common place for a link to a website contact form is in the menu, so this is where we are going to add a link for our contact form. As the links are in our base template, we need to modify base.html. Pause the video and enter the code into your editor, or copy from your transcript:

We have made one change to the base template—in line 25 we have inserted an extra list item that’ll be rendered at the end of the other menu items.

Create the Contact Form Template

For our contact form to render, it needs a template. In your templates\pages\ directory, create a new file called contact.html. Pause the video and enter the code into your editor, or copy from your transcript:

You’ll notice that we are extending the page template this time and replacing the title and content blocks with new content for our contact form.

Some other things to note:

In line 8 we’re using the {% if %} template tag for the first time. submitted is a boolean value that is passed in from the view. The if, else and endif tags are creating a logical branch that’s saying “if the form has been submitted, display the thank you message, otherwise display the blank form.”

Line 14 is the beginning of our POST form. This is standard HTML. Note the novalidate attribute in the form tag. When using HTML5 in some of the latest browsers (notably Chrome), form fields will be automatically validated by the browser. As we want Django to handle form validation, the novalidate attribute tells the browser not to validate the form.

Line 16 is the line that renders the form fields. The as_table method renders the form fields as table rows. Django doesn’t render the table tags or the submit button, so we’re adding these on line 15 and lines 17 to 21.

And on Line 22 we’ve added the {% csrf_token %} template tag. All POST forms that are targeted at internal URLs should use this tag. This is to protect against Cross Site Request Forgeries (CSRF). A full explanation of CSRF is beyond the scope of this course; just rest assured that adding the {% csrf_token %} tag is a Good Thing.

Create the Contact Form View

Our final step is to create the new contact view. Open your views.py file and add the contact view code as follows (changes in bold). Pause the video and enter the code into your editor, or copy from your transcript:

Let’s step through the important bits of this code:

In line 2 we import the HttpResponseRedirect class from django.http and in line 5 we import the ContactForm class from forms.py.
Jumping down to our contact view, in line 21, we check if the form was POSTed. If not, we skip down to line 28 and create a blank form. First time through, line 29 will be False, so the view drops through to line 32 and renders the blank form.

Line 23 checks to see if the form contains valid data. Notice there is no cruft for handling invalid form data. This is what’s really cool about the Form class. If the form is invalid, the view just needs to drop right through to line 32 and re-render the form because Django will automatically add the relevant error messages to your form.

In line 24, We’re loading the data from the form into the variable cd. If the form is valid, Django will normalize the data and save it to a dictionary accessible via the cleaned_data attribute of the Form class. In this context, normalizing means changing it to a consistent format. For example, regardless of what date format you use, Django will always convert a date string to a Python datetime.date object.
We’re not doing anything with the submitted form right now, so I’ve put an assert False statement in line 25 so we can test the form submission with Django’s error page.

Once the form has been submitted successfully, we are using Django’s HttpResponseRedirect class in line 26 to redirect back to the contact view. Adding ?submitted=True to the end of the URL changes the request method from POST to GET. This time, line 21 will be False, so the view will drop down to line 28 which clears the form. As line 29 is now True, we set the submitted variable to True.

Line 32 renders the template and data back to the view. Note the addition of the page_list QuerySet. If you remember from Module 7, the page template needs page_list to be able to render the menu items. The submitted variable is used by the template. When submitted is False, the form shows. When submitted is True, the thank you message is shown.

Now let’s test the form.

Go ahead and uncomment line 25, save the views.py file and then navigate to http://127.0.0.1:8000/contact to see your new contact form. First, note that there is a link to the contact form in the left menu.

Next, submit the empty form to make sure the form validation is working. You should see the error messages display.
Now, fill out the form with valid data and submit again. You should get an assertion error, triggered by the assert False statement in the view (line 25).

When we created our contact form view, we told Django to put the contents of the cleaned_data attribute into the variable cd (line 24). With the assert False active in our view, we can check the contents of cd with the Django error page.

Scroll down to the assertion error and open the Local vars panel. You should see the cd variable containing a dictionary of the complete form submission.

Once you have checked the form is working correctly, click on the “Contact Us” link in the menu to take you back to the empty form.
Our contact form is working great, but it still looks a bit plain—the fields don’t line up well and the error messages don’t really stand out. Let’s make the form prettier with some CSS. Add the following to the end of your main.css file:

Once you have saved the changes to your CSS file, clear your browser cache to load the new css file and submit the empty form. Not only should your form be better laid out, but showing pretty error messages too.

Like the Content? Grab the Book!

Other than providing you with a convenient resource you can print out, or load up on your device, buying the book also helps support this site.

Book is available in PDF, eBook or as a bundle including both PDF and ebook, plus the source code.

Emailing the Form Data

Our contact form is working well and looking good, but it’s not much use right now because we aren’t doing anything with the form data.

As this is a contact form, the most common way to deal with form submissions is to email them to a site administrator or some other contact person within the organization.

Setting up an email server to test emails in development can be a real pain. Luckily, this is another problem for which the Django developers have provided a handy solution. Django provides a number of email backends, including a few specifically designed for use during development.

We’ll be using the console backend. This backend is particularly useful in development as it doesn’t require you to set up an email server while you are developing a Django application. The console backend sends email output to the terminal (console). You can check this in your terminal window after you submit your form.

There are other email back ends for testing:
• filebased, sends your emails to a file on your local system
• locmem saves it in an attribute in memory; and
• dummy sends it to a dummy back end.

I’ve added links to more information on email backends in the Resources section.

So, let’s go ahead and modify the contact view to send emails. Pause the video and enter the code into your editor, or copy from your transcript.

Let’s have a look at the changes we’ve made:

In line 3 we import the send_mail and get_connection functions from django.core.mail.

In line 26 we need to comment out the assert False statement. if we don’t do this, we’ll keep getting the Django error page.

And in lines 28 to 34 we’re using the send_mail function to send the email.

This is all you need to send an email in Django. All there is to do to send emails once the site is live is to change the backend to a proper mail server and add the mail servers settings to settings.py.

Test the view by filling out the form and submitting. If you look in the console window you’ll see that the view sent the coded email straight to the console. For example, when I submitted the form, this was what Django output to PowerShell.

Now that the form is complete, you will also note that when you enter valid data and submit the form, the contact view redirects to the contact page with “submitted=True” as a GET parameter— http://127.0.0.1:8000/contact?submitted=True.

With submitted set to True, the contact.html template will execute the first {% if %} block in the template and render the success message instead of the form.

Up Next…

Scroll to Top