Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
119 views

Django's Forms - Python Django Tutorials

The document discusses Django's forms functionality. It explains that Django's Form class makes creating and handling forms much easier than coding them from scratch. The Form class handles validation, rendering HTML, and displaying errors without requiring custom code. The document then provides an example of creating a ContactForm class with different field types and labels. It describes the steps needed to add the form to the site, including adding a URL pattern, link in the template, creating a template for the form, and writing a view to process it.

Uploaded by

Nagib Otaybi
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
119 views

Django's Forms - Python Django Tutorials

The document discusses Django's forms functionality. It explains that Django's Form class makes creating and handling forms much easier than coding them from scratch. The Form class handles validation, rendering HTML, and displaying errors without requiring custom code. The document then provides an example of creating a ContactForm class with different field types and labels. It describes the steps needed to add the form to the site, including adding a URL pattern, link in the template, creating a template for the form, and writing a view to process it.

Uploaded by

Nagib Otaybi
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 31

Django’s Forms

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 simple, for example:

<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.

While creating a basic form is simple, things get more complicated


once you need to use the form in a real-life situation. In a live
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 need to check if 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 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.

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 flexible in its approach
to form creation and management—if you 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 you do this. Unless you have a special
application in mind, Django has many tools and libraries that make
form building much easier. In particular, Django’s Form class offers a
very convenient set of class methods to take care of most of the
form processing and validation for you.

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:

(env_myclub) ...\myclub_root> python manage.py shell

Once the shell is running, create your SimpleForm class:

1 >>> from django import forms


2 >>> class SimpleForm(forms.Form):
3 ... firstname = forms.CharField(max_length=100)
4 ... lastname = forms.CharField(max_length=100)
5 ...
6 >>>

Let’s have a look at what we did here:

Line 1. To use the Form class, we need to import the forms


module from Django.
Line 2. We create our SimpleForm class, which inherits from
Django’s forms.Form class.
Lines 3 and 4 are the firstname and lastname fields from 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 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>
>>>

Let’s see what’s happening here:

Line 1 should be easy enough to follow—we have created an


instance of the SimpleForm class and named the instance f.
Line 2 is where the Django magic happens. 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.
Try these out for yourself:

>>> print(f.as_ul())
>>> print(f.as_table())

Note Django doesn’t generate the <form></form> element for you,


nor does it generate the <ul></ul> or <table></table> elements or
the submit button. This is because they are structural elements on
your page, so they should remain in the template.

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.']}

Reviewing what we did this time:

Line 1. We created a new instance of the SimpleForm class and


passed an empty dictionary ({}) to the form.
Line 2. 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.
Line 4. Finally, if form validation fails, 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. 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.

Creating a Contact Form


To create our ContactForm class, we first create a new file called
contact.py. As the contact form is a part of the website, we will add
it to the site app (new file):
# myclub_root\myclub_site\contact.py

1 from django import forms


2
3 class ContactForm(forms.Form):
4 yourname = forms.CharField(max_length=100, label='Your Name')
5 email = forms.EmailField(required=False, label='Your Email
Address')
6 subject = forms.CharField(max_length=100)
7 message = forms.CharField(widget=forms.Textarea)

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.

Now we have 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 site URLs;


2. Add navigation to our site template;
3. Create a template for the contact form; and
4. Create a new view to manage the contact form.

Add Contact Form URL to Site App

To show our contact form, we start by creating a URL for it. To do


that, we need to modify our site’s urls.py file (changes in bold):

# myclub_root\myclub_site\urls.py

1 from django.contrib import admin


2 from django.urls import include, path
3 from django.contrib.auth import views as auth_views
4 from . import contact
5
6 urlpatterns = [
7 path('admin/', admin.site.urls),
8 path('contact/', contact.contact, name='contact'),
# ...

In line 4 we’ve imported the contact module and in line 8 we have


added a URLconf that will direct the URL ending in “contact” to the
new contact view we will write shortly.

Add Navigation to Site Template

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>
# ...

I have made one change to the base.html template—in line 5 we


have turned the placeholder into a link to the contact page. I’ve also
reformatted the code slightly.

Create the Contact Form Template

For our contact form to render, it needs a template. In your site


templates folder, create a new folder called “contact”. Inside the
new folder, create a file called contact.html and enter the following
template code (new file):

# \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>&nbsp;</td>
19 <td><input type="submit" value="Submit"></td>
20 </tr>
21 </table>
22 {% csrf_token %}
23 </form>
24 {% endif %}
25 {% endblock content %}

In the contact template, we are extending the base template, and


replacing the title and content blocks with new content for our
contact form. Some other things to note:

Line 8. We are using the {% if %} template tag for the first


time. submitted is a boolean value passed in from the view. The
{% if %} / {% else %} / {% endif %} tags (Lines 8, 13 and 24)
are creating a logical branch that is saying “if the form has
been submitted, show the thank you message, otherwise show
the blank form.”
Line 14. Is the start 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. This is the line that renders the form fields. The
as_table method will render the form fields as table rows.
Django doesn’t render the table tags or the submit button, so
we are adding these on line 15 and lines 17 to 21.
Line 22. All POST forms targeted at internal URLs must use the
{% csrf_token %} template tag. This is to protect against
Cross-Site Request Forgeries (CSRF). A full explanation of
CSRF is beyond the scope of this book; just rest assured that
adding the {% csrf_token %} tag is a Good Thing.

Create the Contact Form View

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

1 from django import forms


2 from django.shortcuts import render
3 from django.http import HttpResponseRedirect
4
5
6 class ContactForm(forms.Form):
7 yourname = forms.CharField(max_length=100, label='Your Name')
8 email = forms.EmailField(required=False,label='Your e-mail
address')
9 subject = forms.CharField(max_length=100)
10 message = forms.CharField(widget=forms.Textarea)
11
12
13 def contact(request):
14 submitted = False
15 if request.method == 'POST':
16 form = ContactForm(request.POST)
17 if form.is_valid():
18 cd = form.cleaned_data
19 # assert False
20 return HttpResponseRedirect('/contact?submitted=True')
21 else:
22 form = ContactForm()
23 if 'submitted' in request.GET:
24 submitted = True
25
26 return render(request,
27 'contact/contact.html',
28 {'form': form, 'submitted': submitted}
29 )

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

Line 2. Import the render() shortcut function from


django.shortcuts.
Line 3. Import the HttpResponseRedirect class from
django.http.
Line 13. The beginning of our new contact view.
Line 15. Check if the form was POSTed. If not, skip down to
line 22 and create a blank form.
Line 17. Check 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
drops right through to line 26 and simply re-renders the form
as Django has already automatically added the relevant error
messages to the form.
Line 18. 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 entry
format you use, Django will always convert a date string to a
Python datetime.date object.
Line 19. We’re not doing anything with the submitted form right
now, so we put in an assertion error to test the form submission
with Django’s error page.
Line 20. Once the form has been submitted successfully, we
are using Django’s HttpResponseRedirect class to redirect back
to the contact view. We set the submitted variable to True, so
instead of rendering the form, the view will render the thank
you message.
Line 26. Renders the template and data back to the view.

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).

Figure 8-4: Using the assert False statement allows us to check


the contents of the submitted form.

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.

Add Styles to the Contact 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.

Emailing the Form Data

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.

Setting up an email server to test emails in development can be


painful. Luckily, this is another problem for which the Django
developers have provided a handy solution. Django provides several
email backends, including a few designed for use during
development.

We will use the console backend. This backend is 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 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.

You can find more information in the Django documentation under


Email Backends.

So, let’s modify the contact view to send emails (changes in bold):

# myclub_root\myclub_site\contact.py

1 from django import forms


2 from django.shortcuts import render
3 from django.http import HttpResponseRedirect
4 from django.core.mail import send_mail, get_connection
5
6
7 class ContactForm(forms.Form):
8 yourname = forms.CharField(max_length=100, label='Your Name')
9 email = forms.EmailField(required=False,label='Your e-mail
address')
10 subject = forms.CharField(max_length=100)
11 message = forms.CharField(widget=forms.Textarea)
12
13
14 def contact(request):
15 submitted = False
16 if request.method == 'POST':
17 form = ContactForm(request.POST)
18 if form.is_valid():
19 cd = form.cleaned_data
20 # assert False
21 con =
get_connection('django.core.mail.backends.console.EmailBackend')
22 send_mail(
23 cd['subject'],
24 cd['message'],
25 cd.get('email', 'noreply@example.com'),
26 ['siteowner@example.com'],
27 connection=con
28 )
29 return HttpResponseRedirect('/contact?submitted=True')
30 else:
31 form = ContactForm()
32 if 'submitted' in request.GET:
33 submitted = True
34
35 return render(request, 'contact/contact.html', {'form': form,
'submitted': submitted})

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

Line 4. Import the send_mail() and get_connection()


functions from django.core.mail.
Line 20. Comment out the assert False statement. If we don’t
do this, we will keep getting Django’s error page.
Lines 21 to 28. Open a connection to the email backend and
use the send_mail() function to send the email.

This is all you need to send an email in Django. To switch to


production, you only need to change the backend and add your
email server settings to settings.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):

Content-Type: text/plain; charset="utf-8"


MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: this is the subject
From: nigel@masteringdjango.com
To: siteowner@example.com
Date: Fri, 22 May 2020 23:50:39 -0000
Message-ID: <...@DESKTOP.home>

This is the message


------------------------------------------

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 wants to allow venue managers to add their venue to the


club database. We can’t give venue managers admin access to the
backend, so we will create a form they can fill out in the frontend.

The process is as follows:

1. Create the VenueForm model form for collecting venue


information from the user.
2. Add a new view to manage the form.
3. Create the form template; and
4. Add the new view and form to our urls.py file and update the
site template to link to the venue form.

Create the Venue Form

Creating a form for the model is where the power of Django’s


ModelForm class really shines, as it’s an almost trivial task. Create a
new forms.py file in your events app and enter the following code
(new file):

# \myclub_root\events\forms.py

1 from django import forms


2 from django.forms import ModelForm
3 from .models import Venue
4
5 class VenueForm(ModelForm):
6 required_css_class = 'required'
7 class Meta:
8 model = Venue
9 fields = '__all__'

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:

Line 2. We import the ModelForm class, which does all the


heavy lifting for us.
Line 3. We import our Venue model.
Line 5. Our VenueForm class inherits from ModelForm
Line 6. required_css_class is a handy ModelForm class option
that adds a CSS class to our required fields. We will use this
class to add an asterisk (*) to the required fields in the form
template.
Line 7. The ModelForm class has an internal Meta class which we
use to pass in the metadata options the ModelForm class needs
to render our form:
Line 8. The model on which to base our form; and
Line 9. The model fields to render on the form. Here, we’re
using the special __all__ value to tell Django to use all the
form fields.

Add the Venue View

The view to add a venue builds on what we learned previously in the


chapter. We will call the new view add_venue, so let’s add the view
code to the views.py file in our events app (changes in bold):

# \myclub_root\events\views.py

1 from django.shortcuts import render


2 from django.http import HttpResponse
3 from django.http import HttpResponseRedirect
4 from datetime import date
5 import calendar
6 from calendar import HTMLCalendar
7 from .models import Event
8 from .forms import VenueForm
9
10 # ...
11
12 def add_venue(request):
13 submitted = False
14 if request.method == 'POST':
15 form = VenueForm(request.POST)
16 if form.is_valid():
17 form.save()
18 return HttpResponseRedirect('/add_venue/?
submitted=True')
19 else:
20 form = VenueForm()
21 if 'submitted' in request.GET:
22 submitted = True
23 return render(request,
24 'events/add_venue.html',
25 {'form': form, 'submitted': submitted}
26 )

At the top of the file, we’re importing HttpResponseRedirect from


django.http (line 3) and the form (VenueForm) from forms.py (line
8). This view is functionally identical to the view for our contact
form, except we have removed the code for emailing the form data
and replaced it with the form.save() method to save the form data
to our database (line 17).

Create the Venue Form Template

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>&nbsp;</td>
18 <td><input type="submit" value="Submit"></td>
19 </tr>
20 </table>
21 {% csrf_token %}
22 </form>
23 {% endif %}
24 {% endblock content %}

Except for some contextual changes to the display text, this


template is identical to the contact form template, so I won’t go over
any details.

While we are working on the form template, we need to add a little


tweak to the main.css file, so our required field labels will have an
asterisk appended to the label:

# add to the end of \static\main.css

.required label:after {
content: "*";
}

Link to the Add Venue Form

The last task to complete is to provide a new URL configuration for


the events app and add an HTML anchor to the base template to
link our website to the new add venue form.

First, let’s add the URL configuration to the events app’s urls.py file
(changes in bold):

# \myclub_root\events\urls.py

1 from django.urls import path, re_path


2 from . import views
3
4 urlpatterns = [
5 path('', views.index, name='index'),
6 path('add_venue/', views.add_venue, name='add-venue'),
7 # ...
8 ]

One change here—I’ve added a new path() function on line 6 which


will redirect a URL ending with add_venue/ to the add_venue view.

And now for the base template (change in bold):

# \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.

Overriding Form Methods

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

1 from django import forms


2 from django.forms import ModelForm
3 from .models import Venue
4
5 class VenueForm(ModelForm):
6 required_css_class = 'required'
7 class Meta:
8 model = Venue
9 fields = '__all__'
10
11 def clean(self):
12 cleaned_data = super().clean()
13 phone = cleaned_data.get("phone")
14 email_address = cleaned_data.get("email_address")
15 if not (phone or email_address):
16 raise forms.ValidationError(
17 "You must enter either a phone number or an email,
or both."
18 )

Let’s have a closer look at the new method we’ve added to the
VenueForm class:

Line 12. super().clean() maintains any validation logic from


parent classes.
Lines 13 and 14. We’re retrieving the values for the venue
phone number and email address from the dictionary saved in
the cleaned_data attribute.
Line 15. If both the phone number and email address are blank,
a validation error is raised (line 16).

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).

Figure 8-8: Overriding the clean() method of the Form class


provides a painless way to add custom validation to your form.

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.

This chapter concludes Part 1 of the book. In the next chapter, we


will move on from the fundamentals of Django to dig deeper into
models, views, and templates, and cover more advanced topics on
the Django admin, forms and user management.

You might also like