Django's Forms - Python Django Tutorials
Django's Forms - Python Django Tutorials
<form>
<p>First Name: <input type="text" name="firstname"></p>
<p>Last Name: <input type="text" name="lastname"></p>
<p><input type="submit" value="Submit"></p>
</form>
The <form></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. I have rendered
the form elements as paragraphs in this example, but it’s also
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 were to
render this form in a webpage, it would look like Figure 8-1.
Figure 8-1: A simple HTML form.
You must also ensure your form deals with entered data safely. A
common way hackers target a website is to submit malicious
program code via forms to attempt to hack into the site.
With the 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, and an associated HTML widget.
Let’s explore the Form class further with the Django interactive shell.
From within your virtual environment, run the command:
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 go back to the shell:
1 >>> f = SimpleForm()
2 >>> print(f.as_p())
<p><label for="id_firstname">Firstname:</label> <input type="text"
name="firstname" maxlength="100" required id="id_firstname"></p>
<p><label for="id_lastname">Lastname:</label> <input type="text"
name="lastname" maxlength="100" required id="id_lastname"></p>
>>>
>>> print(f.as_ul())
>>> print(f.as_table())
Django’s Form class also handles validation for you. Let’s go back to
the shell to try this out:
1 >>> f = SimpleForm({})
2 >>> f.is_valid()
3 False
4 >>> f.errors
{'firstname': ['This field is required.'], 'lastname': ['This field
is required.']}
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. Let’s try this out in the shell:
>>> print(f.as_p())
<ul class="errorlist"><li>This field is required.</li></ul>
<p><label for="id_firstname">Firstname:</label> <input type="text"
name="firstname" maxlength="100" required id="id_firstname"></p>
<ul class="errorlist"><li>This field is required.</li></ul>
<p><label for="id_lastname">Lastname:</label> <input type="text"
name="lastname" maxlength="100" required id="id_lastname"></p>
>>>
You can see Django added the errors to the form as unordered lists.
If you were to render this form in your browser, it would look
something like Figure 8-2.
Figure 8-2: Django’s Form class renders the error messages to the
form automatically.
Now we’ve had a look at how Django’s Form class works, let’s create
our first form for the website. We will start with a simple form
common to most websites—a contact form.
This is like the SimpleForm class we created in the shell, with some
differences:
Line 4. If you don’t specify the label attribute, Django uses the
field name for the field label. We want the label for the yourname
field to be more readable, so we set the label attribute to “Your
Name”.
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 are also
changing the default label of the email field to “Your e-mail
address”.
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.
# myclub_root\myclub_site\urls.py
We added a placeholder for the contact page in the top menu when
we created the base template. Now we will turn the placeholder into
a link for our contact form (changes in bold):
# myclub_site\templates\base.html
# ...
1 <header id="header">
2 <div id="logo"><img src="{% static 'logo.png' %}" alt="" />
</div>
3 <div id="top_menu">
4 Home | Calendar | About |
5 <a href="/contact">Contact Us</a>
6 </div>
# ...
# \myclub_site\templates\contact\contact.html
1 {% extends "base.html" %}
2
3 {% block title %}Contact Us{% endblock title %}
4
5 {% block content %}
6 <h1>Contact us</h1>
7
8 {% if submitted %}
9 <p class="success">
10 Your message was submitted successfully. Thank you.
11 </p>
12
13 {% else %}
14 <form action="" method="post" novalidate>
15 <table>
16 {{ form.as_table }}
17 <tr>
18 <td> </td>
19 <td><input type="submit" value="Submit"></td>
20 </tr>
21 </table>
22 {% csrf_token %}
23 </form>
24 {% endif %}
25 {% endblock content %}
Our last step is to create the new contact view. Open your
contact.py file and add the contact view code (changes in bold):
# myclub_root\myclub_site\contact.py
To test the contact form, uncomment line 19, save the contact.py
file and then navigate to http://127.0.0.1:8000/contact to see
your new contact form. First, note there is a link to the contact form
in the top menu.
Next, submit the empty form to make sure the form validation is
working. Django should show the error messages (Figure 8-3).
Figure 8-3: The contact form showing errors for required fields.
Now, fill out the form with valid data and submit it again. You should
get an assertion error triggered by the assert False statement in
the view (line 19). When we wrote our contact form view, we told
Django to put the contents of the cleaned_data attribute into the
variable cd (line 18).
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 (Figure 8-
4).
Once you have checked the submitted data is correct, click the
back button in your browser and then 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 stand out.
Let’s make the form prettier with some CSS. Add the following to
the end of your main.css file:
# \myclub_site\static\main.css
# ...
ul.errorlist {
margin: 0;
padding: 0;
}
.errorlist li {
border: 1px solid red;
color: red;
background: rgba(255, 0, 0, 0.15);
list-style-position: inside;
display: block;
font-size: 1.2em;
margin: 0 0 3px;
padding: 4px 5px;
text-align: center;
border-radius: 3px;
}
input, textarea {
width: 100%;
padding: 5px!important;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
border-radius: 3px;
border-style: solid;
border-width: 1px;
border-color: rgb(169,169,169)
}
input {
height: 30px;
}
.success {
background-color: rgba(0, 128, 0, 0.15);
padding: 10px;
text-align: center;
color: green;
border: 1px solid green;
border-radius: 3px;
}
Once you have saved the changes to your CSS file, refresh the
browser and submit the empty form. You may have to clear the
browser cache to reload your CSS file. Not only should your form be
better laid out, but showing pretty error messages too (Figure 8-5).
Figure 8-5: Adding some CSS changes our rather plain contact
form into something to be proud of.
Our contact form works well and looks 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.
There are other email backends for testing: filebased, locmem and
dummy, which send your emails to a file on your local system, save it
in an attribute in memory or send to a dummy backend respectively.
So, let’s modify the contact view to send emails (changes in bold):
# myclub_root\myclub_site\contact.py
Test the view by filling out the form and submitting. If you look in the
console window (PowerShell or command prompt) you will see the
view sent the coded email straight to the console. For example,
when I submitted the form, this was what Django output to
PowerShell (I’ve shortened some of the longer lines):
Now the form is complete, submit the form with valid data and the
contact view will redirect 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 and render the
success message instead of the form (Figure 8-6).
Figure 8-6: Once a valid form has been submitted, the thank you
message is shown instead of the form.
Model Forms
The contact form is a common, but simple use of a website form.
Another common use for forms is to collect information from the
user and save the information in the database. Examples include
entering your personal information for a membership application,
your name and address details for a sales order, and filling out a
survey.
Using forms for collecting and saving data from users is so common
Django has a special form class to make creating forms from Django
models much easier—model forms. With model forms, you create a
Django model and then create a form that inherits from Django’s
ModelForm class. As always, the devil is in the detail, so let’s create a
form for our Venue model.
# \myclub_root\events\forms.py
That’s it—a few lines of code is all Django needs to create a form for
your model to show the necessary HTML on the page, validate your
form fields, and pass form data to your view.
There are some things to note, however, so let’s look at those now:
# \myclub_root\events\views.py
Now it’s time to create the template for our form. We will inherit from
the site’s base template, so the form is very similar to the contact
form template. Create a new file called add_venue.html and add it to
the events app’s templates folder (new file):
# \events\templates\events\add_venue.html
1 {% extends "base.html" %}
2
3 {% block title %}Add Venue{% endblock title %}
4
5 {% block content %}
6 <h1>Add your venue to our database</h1>
7
8 {% if submitted %}
9 <p class="success">
10 Your venue was submitted successfully. Thank you.
11 </p>
12 {% else %}
13 <form action="" method="post" novalidate>
14 <table>
15 {{ form.as_table }}
16 <tr>
17 <td> </td>
18 <td><input type="submit" value="Submit"></td>
19 </tr>
20 </table>
21 {% csrf_token %}
22 </form>
23 {% endif %}
24 {% endblock content %}
.required label:after {
content: "*";
}
First, let’s add the URL configuration to the events app’s urls.py file
(changes in bold):
# \myclub_root\events\urls.py
# \myclub_site\templates\base.html
1 <aside id="rightsidebar">
2 <nav id="nav">
3 <ul>
4 <li><a href="/add_venue">Add Your Venue</a></li>
5 <li>Menu 2</li>
6 <li>Menu 3</li>
7 </ul>
8 </nav>
9 </aside>
Here, in line 4, I’ve added a simple HTML anchor tag so the first
menu item on the right sidebar links to our new add venue form.
Save all your files and fire up the development server. When you
open your site, there should be a link to add a venue in the right
sidebar. Click this link, and you should see your new add a venue
form (Figure 8-7).
Figure 8-7: The new form for adding a venue.
While our new form for adding venues is functional, it has a problem
—both contact phone and contact email address fields are optional.
This was OK in the admin because a staff member would know that
MyClub needs at least one form of contact in the database. User
experience design best-practice says you should never assume site
visitors know how to fill out a form correctly. So, we need to add
custom validation to ensure the person filling out the form enters
either a phone number or an email address.
As a part of the validation process, both regular forms and model
forms run the clean() method. You can override this special method
to provide custom validation. To override the clean() method in a
ModelForm class, we add the override to the class (changes in bold):
# \myclub_root\events\forms.py
Let’s have a closer look at the new method we’ve added to the
VenueForm class:
You can see how this new custom validation works in practice if you
try to submit the form with both the phone number and email
address fields blank (Figure 8-8).
Chapter Summary
In this chapter, we learned the fundamentals of how to create and
manage forms in Django. We learned how to create a contact form
for collecting feedback from site visitors, and how to create a form
for a model, so users can submit data to the site database in the
frontend.