Django Form Basics
Last updated on July 27, 2020
The syntax of creating forms in Django is almost similar to that of creating models, the only differences are:
- Django form inherits from
forms.Form
instead ofmodels.Model
. - Each form fields inherit
forms.FieldName
instead ofmodels.FieldName
.
Let's start off by creating an AuthorForm
class.
Create a new file called forms.py
, if not already exists in the blog app i.e TGDB/django_project/blog
(same place where models.py
file is located) and add the following code to it.
TGDB/django_project/blog/forms.py
1 2 3 4 5 6 7 8 9 | from django import forms
class AuthorForm(forms.Form):
name = forms.CharField(max_length=50)
email = forms.EmailField()
active = forms.BooleanField(required=False) # required=False makes the field optional
created_on = forms.DateTimeField()
last_logged_in = forms.DateTimeField()
|
Form fields are similar to model fields in the following ways:
- Both correspond to a Python type.
- Both validate data in the form.
- Both fields are required by default.
- Both types of fields know how to represent them in the templates as HTML. Every form fields are displayed in the browser as an HTML widget. Each form field is assigned a reasonable Widget class, but you can also override this setting.
Here is an important difference between the model fields and form fields.
Model fields know how to represent themselves in the database whereas form fields do not.
Form States #
A form in Django can be either in a Bound state or Unbound state. What is Bound and Unbound state?
Unbound State: In Unbound state the form has no data associated with it. For example, an empty form displayed for the first time is in unbound state.
Bound State: The act of giving data to the form is called binding a form. A form is in the bound state if it has user submitted data. It doesn't matter whether the data is valid or not.
If the form is in bound state but contains invalid data then the form is bound and invalid. On the other hand, if the form is bound and data is valid then the form is bound and valid.
is_bound attribute and is_valid() method #
We can use is_bound
attribute to know whether the form is inbound state or not. If the form is in the bound state then the is_bound
returns True
, otherwise False
.
Similarly, we can use the is_valid()
method to check whether the entered data is valid or not. If the data is valid then is_valid()
returns True
, otherwise False
. It is important to note that if is_valid()
returns True
then is_bound
attribute is bound to return True
.
Accessing Cleaned Data #
When a user enter submits the data via form Django first validate and clean the data. Does this mean that the data entered by the user were not clean? Yes, for the two good reasons:
When it comes to submitting data using forms you must never trust the user. It takes a single malicious user to wreak havoc on your site. That's why Django validates the form data before you can use them.
Any data the user submits through a form will be passed to the server as strings. It doesn't matter which type of form field was used to create the form. Eventually, the browser would will everything as strings. When Django cleans the data it automatically converts data to the appropriate type. For example
IntegerField
data would be converted to an integer,CharField
data would be converted to a string,BooleanField
data would be converted to a bool i.eTrue
orFalse
and so on. In Django, this cleaned and validated data is commonly known as cleaned data. We can access cleaned data viacleaned_data
dictionary:cleaned_date['field_name']
You must never access the data directly using
self.field_name
as it may not be safe.
Django Forms in Django Shell #
In this section we will learn how to bind data and validate a form using Django Shell. Start Django Shell by typing python manage.py shell
command in the command prompt or terminal. Next, import the AuthorForm
class and instantiate a AuthorForm
object:
1 2 3 4 5 6 7 8 | (env) C:\Users\Q\TGDB\django_project>python manage.py shell
Python 3.4.4 (v3.4.4:737efcadf5a6, Dec 20 2015, 20:20:57) [MSC v.1600 64 bit (AM
D64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from blog.forms import AuthorForm
>>> f = AuthorForm()
>>>
|
At this point, our form object i.e f
is unbound because there is no data in the form. We can verify this fact by using is_bound
attribute.
1 2 3 4 | >>>
>>> f.is_bound
False
>>>
|
As expected is_bound
attribute returns False
. We can also check whether the form is valid or not by calling is_valid()
method.
1 2 3 4 | >>>
>>> f.is_valid()
False
>>>
|
It is important to understand that is_bound
and is_valid()
are related. If is_bound
is False
then is_valid()
will always return False
no matter what. Similarly, if is_valid()
returns True
then is_bound
must be True
.
Calling is_valid()
method results in validation and cleaning of the form data. In the process, Django creates an attribute called cleaned_data
, a dictionary which contains cleaned data only from the fields which have passed the validation tests. Note that cleaned_data
attribute will only be available to you after you have invoked the is_valid()
method. Trying to access cleaned_data
before invoking is_valid()
will throw an AttributeError
exception.
Obviously, now the question arises "How do we bind data to the form"?
To bind data to a form simply pass a dictionary as an argument to the form class(in this case AuthorForm
) while creating a new form object.
1 2 3 4 5 6 7 8 9 10 | >>>
>>> data = {
... 'name': 'jon',
... 'created_on': 'today',
... 'active': True,
... }
>>>
>>>
>>> f = AuthorForm(data)
>>>
|
Our form object f
has data now, so we can say that it is bound. Let's verify that by using is_bound
attribute.
1 2 3 4 | >>>
>>> f.is_bound
True
>>>
|
As expected, our form is bound now. We could also get a bound form by passing an empty dictionary ({}
).
1 2 3 4 5 6 | >>>
>>> data = {}
>>> f2 = AuthorForm(data)
>>> f2.is_bound
True
>>>
|
Okay let's now try accessing cleaned_data
attribute before invoking is_valid()
method.
1 2 3 4 5 6 7 | >>>
>>> f.cleaned_data
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'CategoryForm' object has no attribute 'cleaned_data'
>>>
>>>
|
As expected, we got an AttributeError
exception. Now we will validate the form by calling is_valid()
method.
1 2 3 4 5 6 7 | >>>
>>> f.is_valid()
False
>>>
>>> f.cleaned_data
{'active': True, 'name': 'jon'}
>>>
|
Our validation fails but we now have cleaned_data
dictionary available. Notice that there is no created_on
key in the cleaned_data
dictionary because Django failed to validate this field. In addition to that, the form validation also failed to validate email
and last_logged_in
field of the AuthorForm
because we haven't provided any data to it.
Always remember that the cleaned_data
attribute will only contain validated and cleaned data, nothing else.
To access errors, the form object provides an errors
attribute which is an object of type ErrorDict
, but for the most part you can use it as a dictionary. Here is how it works:
1 2 3 4 5 6 | >>>
>>> f.errors
{'created_on': ['Enter a valid date/time.'], 'email': ['This field is required.'
], 'last_logged_in': ['This field is required.']}
>>>
>>>
|
Notice that there are three fields which failed the validation process. By default, f.errors
returns error messages for all the fields which failed to pass validation. Here is how to get the error message for a particular field.
1 2 3 4 5 6 7 8 9 10 11 12 | >>>
>>> f.errors['email']
['This field is required.']
>>>
>>>
>>> f.errors['created_on']
['Enter a valid date/time.']
>>>
>>>
>>> f.errors['last_logged_in']
['This field is required.']
>>>
|
The errors
object provides two methods to ouput errors in different formats:
Method | Explanation |
---|---|
as_data() |
returns a dictionary with ValidationError object instead of a string. |
as_json() |
returns errors as JSON |
1 2 3 4 5 6 | >>>
>>>
>>> f.errors
{'created_on': ['Enter a valid date/time.'], 'email': ['This field is required.'
], 'last_logged_in': ['This field is required.']}
>>>
|
1 2 3 4 5 6 7 | >>>
>>> f.errors.as_data()
{'created_on': [ValidationError(['Enter a valid date/time.'])], 'email': [Valida
tionError(['This field is required.'])], 'last_logged_in': [ValidationError(['Th
is field is required.'])]}
>>>
>>>
|
1 2 3 4 5 6 7 | >>>
>>> f.errors.as_json()
'{"created_on": [{"code": "invalid", "message": "Enter a valid date/time."}], "e
mail": [{"code": "required", "message": "This field is required."}], "last_logge
d_in": [{"code": "required", "message": "This field is required."}]}'
>>>
>>>
|
Note that unlike the cleaned_data
attribute the errors
attribute is available to you all the time without first calling the is_valid()
method. But there is a caveat, trying to access errors
attribute before calling is_valid()
method results in validation and cleaning of form data first, consequently creating cleaned_data
attribute in the process. In other words, trying to access errors
attribute first will result in a call to is_valid()
method implicitly. However, in your code should always call is_valid()
method explicitly.
To demonstrate the whole process one more time, let's create another form object, but this time we will bind the form with data that will pass the validation.
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 | >>>
>>> import datetime
>>>
>>>
>>> data = {
... 'name': 'tim',
... 'email': 'tim@mail.com',
... 'active': True,
... 'created_on': datetime.datetime.now(),
... 'last_logged_in': datetime.datetime.now()
... }
>>>
>>>
>>> f = AuthorForm(data)
>>>
>>>
>>> f.is_bound
True
>>>
>>>
>>> f.is_valid()
True
>>>
>>>
>>> f.cleaned_data
{'name': 'tim', 'created_on': datetime.datetime(2017, 4, 29, 14, 11, 59, 433661,
tzinfo=<UTC>), 'last_logged_in': datetime.datetime(2017, 4, 29, 14, 11, 59, 433
661, tzinfo=<UTC>), 'email': 'tim@mail.com', 'active': True}
>>>
>>>
>>> f.errors
{}
>>>
|
Digging deep into Form Validation #
When is_valid()
method is called Django does the following things behind the scenes:
The first step is to call Field's
clean()
method. Every form field has aclean()
method, which does the following two things:Convert the field data (recall that the data is sent by the browser as a string to the server) to the appropriate Python type. For example, if the field is defined as
IntegerField
then theclean()
method will convert the data to Pythonint
, if it fails to do so, it raises aValidationError
exception.Validate the converted data received from the step 1. If validation succeeds, cleaned and validated data is inserted into the
cleaned_data
attribute. If it fails aValidationError
is raised. We usually don't override field'sclean()
method.
In step 2, Field's
clean_<fieldname>()
method is called to provide some additional validation to the field. Notice that<fieldname>
is a placeholder, it is not an actual Python method. By default, Django doesn't define these methods. These methods are usually written by developers to provide some additional validation to the field. They do not accept any arguments, but they must return the new value of the field. This method is called only whenValidationError
is not raised by the Field'sclean()
method. That means, if this method is called, it is guaranteed that the field's data is cleaned and validated. Consequently, you must always access field's data inside this method usingcleaned_data['fieldname']
. The value returned by this method replaces the existing value of the field in thecleaned_data
dictionary.
Django repeats step 1 and 2 for all form fields.
Finally, Form's class clean()
method is called. If you want to perform validation which requires access to multiple fields override this method in your form class.
Note: This is an oversimplified view of Django Validation Process. The reality is much more involved but that's enough, to begin with.
Let's take an example to understand how Django performs cleaning and validation when is_valid()
method is called on AuthorForm
class.
1st step - The name
field's clean()
method is called to clean and validate data. On success, it puts the clean and validated data into the cleaned_data
dictionary. If cleaning or validation failed ValidationError
exception is raised and call to clean_name()
is skipped. Nonetheless, the clean()
method of the following field will be called.
2nd step - The name
field's clean_name()
method is called (assuming this method is defined in the form class and ValidationError
is not raised in step 1) to perform some additional validations.
3rd step - The email
field's clean()
method is called to clean and validate data. On success, it puts the clean and validated data into the cleaned_data
dictionary. If cleaning or validation failed ValidationError
exception is raised and call to clean_email()
method is skipped. Nonetheless, the clean()
method of the following field will be called.
4th step - The email
field's clean_email()
is a method called (assuming this method is defined in the form class and ValidationError
is not raised in step 3) to perform some additional validation. At this point, it is guaranteed that email
field is cleaned and validated, so the following code is perfectly valid inside clean_email()
method.
email= self.cleaned_data['email']; ## that's okay
However, there is no guarantee that the data from other fields, for example, the name field is available inside clean_email()
method. So, you should not attempt to access name
field inside clean_email()
method like this:
name = self.cleaned_data['name']; # Not good, you may get an error for doing this
If you want to provide additional validation to the name
field, do it in the clean_name()
method because it is guaranteed to be available there.
This process repeats for every form field. At last, Form's clean()
method or its override is called. An important thing to remember about the Form's clean()
method is that none of the fields is guaranteed to exists here. To access field data you must always use dictionary's object get()
method like this:
self.cleaned_data.get('name')
If the name
key is not available in the cleaned_data
dictionary then get()
method will return None
.
Implementing Custom Validators #
In this section, we are going to implement some custom validators in our AuthorForm
class. Here are things we want to achieve.
- Prevent users to create Author named
"admin"
and"author"
. - Save the email in lowercase only. At this point, nothing is stopping us to save the email in uppercase.
Open forms.py
and modify the code as follows:
TGDB/django_project/blog/forms.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | from django import forms
from django.core.exceptions import ValidationError
class AuthorForm(forms.Form):
#...
last_logged_in = forms.DateTimeField()
def clean_name(self):
name = self.cleaned_data['name']
name_l = name.lower()
if name_l == "admin" or name_l == "author":
raise ValidationError("Author name can't be 'admin/author'")
return name_l
def clean_email(self):
return self.cleaned_data['email'].lower()
|
Restart the Django shell for the changes to take effect and then enter the following code. Here we are trying to validate a form where author name is "author"
.
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 blog.forms import AuthorForm
>>>
>>> import datetime
>>>
>>>
>>> data = {
... 'name': 'author',
... 'email': 'TIM@MAIL.COM',
... 'active': True,
... 'created_on': datetime.datetime.now(),
... 'last_logged_in': datetime.datetime.now()
... }
>>>
>>>
>>> f = AuthorForm(data)
>>>
>>>
>>> f.is_bound
True
>>>
>>>
>>> f.is_valid()
False
>>>
>>>
>>> f.cleaned_data
{'last_logged_in': datetime.datetime(2017, 9, 12, 22, 17, 26, 441359, tzinfo=<UT
C>), 'created_on': datetime.datetime(2017, 9, 12, 22, 17, 26, 441359, tzinfo=<UT
C>), 'active': True, 'email': 'tim@mail.com'}
>>>
>>>
>>> f.errors
{'name': ["Author name can't be 'admin/author'"]}
>>>
>>>
|
As expected, form validation failed because "author"
is not a valid author name. In addition to that cleaned_data
contains email
in lowercase, thanks to the clean_email()
method.
Notice that form's errors
attribute returns the same error message we specified in the clean_name()
method. Let's try validating form data once more, this time we will provide valid data in every field.
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 | >>>
>>>
>>> data = {
... 'name': 'Mike',
... 'email': 'mike@mail.com',
... 'active': True,
... 'created_on': datetime.datetime.now(),
... 'last_logged_in': datetime.datetime.now()
... }
>>>
>>>
>>> f = AuthorForm(data)
>>>
>>>
>>> f.is_bound
True
>>>
>>>
>>> f.is_valid()
True
>>>
>>>
>>> f.cleaned_data
{'last_logged_in': datetime.datetime(2017, 9, 12, 22, 20, 25, 935625, tzinfo=<UT
C>), 'name': 'Mike', 'created_on': datetime.datetime(2017, 9, 12, 22, 20, 25, 93
5625, tzinfo=<UTC>), 'active': True, 'email': 'mike@mail.com'}
>>>
>>>
>>> f.errors
{}
>>>
>>>
|
This time validation succeeds because data in every field is correct.
Saving the form data to the database #
So, how do we save data received via the form to the database? Earlier in this chapter, we have already discussed that unlike models fields, form fields don't know how to represent themselves in the database. Further, unlike models.Model
class, the forms.Form
class doesn't provide save()
method to save the form data to the database.
The solution is to implement our own save()
method. There is no restriction on method name you can call it anything you like. Open forms.py
file and add save()
method towards the end of AuthorForm
class:
TGDB/django_project/blog/forms.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | from django import forms
from django.core.exceptions import ValidationError
from .models import Author, Tag, Category, Post
class AuthorForm(forms.Form):
#...
def clean_email(self):
return self.cleaned_data['email'].lower()
def save(self):
new_author = Author.objects.create(
name = self.cleaned_data['name'],
email = self.cleaned_data['email'],
active = self.cleaned_data['active'],
created_on = self.cleaned_data['created_on'],
last_logged_in = self.cleaned_data['last_logged_in'],
)
return new_author
|
Nothing new here, in line 3, we are importing models from the blog app. In lines 12-20, we are defining the save()
method which uses form data to create a new Author
object. Notice that while creating new Author
object we are accessing form data via cleaned_data
dictionary.
Restart the Django shell again and Let's try creating a new Author via AuthorForm
.
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 blog.forms import AuthorForm
>>>
>>> import datetime
>>>
>>> data = {
... 'name': 'jetson',
... 'email': 'jetson@mail.com',
... 'active': True,
... 'created_on': datetime.datetime.now(),
... 'last_logged_in': datetime.datetime.now()
... }
>>>
>>> f = AuthorForm(data)
>>>
>>> f.is_bound
True
>>>
>>> f.is_valid()
True
>>>
>>> f.save()
<Author: jetson : jetson@mail.com>
>>>
>>>
>>> from blog.models import Author
>>>
>>> a = Author.objects.get(name='jetson')
>>>
>>> a.pk
11
>>>
>>> a
<Author: jetson : jetson@mail.com>
>>>
>>>
|
Sure enough, our newly created category object is now saved in the database.
Our form is fully functional. At this point, we could move on to create form classes for the rest of the objects like Post, Tag etc; but there is a big problem.
The problem with this approach is that fields in the AuthorForm
class map closely to that of Author
models. As a result, redefining them in the AuthorForm
is redundant. If we add or modify any field in the Author
model then we would have to update our AuthorForm
class accordingly.
Further, as you might have noticed there are few differences in the way we have defined model fields and form fields. For example:
The email
field in Author
model is defined like this:
email = models.EmailField(unique=True)
On the other hand, the same field in AuthorForm
is defined like this:
email = forms.EmailField()
Notice that the email
field in AuthorForm
doesn't have unique=True
attribute because unique=True
attribute is only defined for the model fields, not for the form fields. One way to solve this problem is to create a custom validator by implementing clean_email()
method like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 | from django import forms
from .models import Author, Tag, Category, Post
class AuthorForm(forms.Form):
#...
def clean_email(self):
email = self.cleaned_data['email'].lower()
r = Author.objects.filter(email=email)
if r.count:
raise ValidationError("{0} already exists".format(email))
return email.lower()
|
Similarly, Form fields don't provide default, auto_add_now
and auto_now
parameters. If you want to implement functionalities provided by these attributes then you would need the write custom validation method for each of these fields.
As you can see, for each functionality provided by the Django models, we would have to add various cleaning methods as well as custom validators. Certainly, this involves a lot of work. We can avoid all these issues by using ModelForm
.
Removing redundancy using ModelForm #
The ModelForm
class allows us to connect a Form
class to the Model
class.
To use ModelForm
do the following:
- Change inheritance of the form class from
forms.Form
toforms.ModelForm
. - Inform the form class in
forms.py
which model to use by using themodel
attribute of theMeta
class.
After these two steps, we can remove all the form fields we have defined in the AuthorForm
class. Furthermore, we can remove the save()
method too, because ModelForm
provides this method. It is important to mention here that the save()
method we implemented earlier in this chapter can only create objects, it can't update them. On the other hand, the save()
method coming from ModelForm
can do both.
Here is the modified code.
TGDB/django_project/blog/forms.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | from django import forms
from django.core.exceptions import ValidationError
from .models import Author, Tag, Category, Post
class AuthorForm(forms.ModelForm):
class Meta:
model = Author
def clean_name(self):
name = self.cleaned_data['name']
name_l = name.lower()
if name_l == "admin" or name_l == "author":
raise ValidationError("Author name can't be 'admin/author'")
return name_l
def clean_email(self):
return self.cleaned_data['email'].lower()
|
There is still one thing missing in our AuthorForm
class. We have to tell AuthorForm
class which fields we want to show in the form. To do that we use fields
attribute of the Meta
class. It accepts a list or tuple of field names you want to show in the form. If you want to show all the fields just use "__all__"
(that's double underscore).
1 2 | fields = '__all__' # display all the fields in the form
fields = ['title', 'content'] # display only title and content field in the form
|
Similarly, there exists a complementary attribute called exclude
which accepts a list of field names which you don't want to show in the form.
exclude = ['slug', 'pub_date'] # show all the fields except slug and pub_date
Let's update our code to use the fields
attribute.
TGDB/django_project/blog/forms.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from django import forms
from django.core.exceptions import ValidationError
from .models import Author, Tag, Category, Post
class AuthorForm(forms.Form):
class Meta:
model = Author
fields = '__all__'
def clean_name(self):
name = self.cleaned_data['name']
name_l = name.lower()
if name_l == "admin" or name_l == "author":
raise ValidationError("Author name can't be 'admin/author'")
return name_l
def clean_email(self):
return self.cleaned_data['email'].lower()
|
Notice that we haven't changed clean_name()
and clean_email()
method because they work with
ModelForm
too.
Additional Validation in ModelForm #
In addition to Form validation, ModelForm
also performs its own validation. What is meant by that? It simply means that ModelForm performs validation at the database level.
ModelForm
Validation takes place in 3 steps.
Model.clean_fields()
- This method validates all the fields in the model
Model.clean()
- Works just like Form's clean()
method. If you want to perform some validation at the database level which requires access to multiple fields override this method in the Model class. By default, this method does nothing.
Model.validate_unique()
- This method checks uniqueness constraints imposed on your model (using unique parameter).
Which validation occurs first Form validation or Model validation?
Form Validation occurs first.
How do I trigger this Model validation?
Just call is_valid()
method as usual and Django will run Form validation followed by ModelForm
validation.
Creating Form classes for the remaning objects #
Before we move ahead, let's create PostForm
, CategoryForm
and TagForm
class in the forms.py
file.
TGDB/django_project/blog/forms.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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #...
from .models import Author, Tag, Category, Post
from django.template.defaultfilters import slugify
#...
class AuthorForm(forms.ModelForm):
#...
class TagForm(forms.ModelForm):
class Meta:
model = Tag
fields = '__all__'
def clean_name(self):
n = self.cleaned_data['name']
if n.lower() == "tag" or n.lower() == "add" or n.lower() == "update":
raise ValidationError("Tag name can't be '{}'".format(n))
return n
def clean_slug(self):
return self.cleaned_data['slug'].lower()
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = '__all__'
def clean_name(self):
n = self.cleaned_data['name']
if n.lower() == "tag" or n.lower() == "add" or n.lower() == "update":
raise ValidationError("Category name can't be '{}'".format(n))
return n
def clean_slug(self):
return self.cleaned_data['slug'].lower()
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'content', 'author', 'category', 'tags',)
def clean_name(self):
n = self.cleaned_data['title']
if n.lower() == "post" or n.lower() == "add" or n.lower() == "update":
raise ValidationError("Post name can't be '{}'".format(n))
return n
def clean(self):
cleaned_data = super(PostForm, self).clean() # call the parent clean method
title = cleaned_data.get('title')
# if title exists create slug from title
if title:
cleaned_data['slug'] = slugify(title)
return cleaned_data
|
The TagForm
and CategoryForm
are very similar to AuthorForm
class but PostForm
is a little different. In PostForm
, we are overriding Form's clean()
method for the first time. Recall that we commonly use Form's clean()
method when we want to perform some validation which requires access to two or more fields at the same time.
In line 56, we are calling Form's parent clean()
method which by itself does nothing, except returning cleaned_data
dictionary.
Next, we are using dictionary object's get()
method to access the title
field of the PostForm
, recall that in the Form's clean()
method none of the fields is guaranteed to exist.
Then we test the value of the title
field. If the title
field is not empty, then we use slugify()
method to create slug from the title
field and assign the result to cleaned_data['slug']
. At last, we return cleaned_data
from the clean()
method.
It is important to note that by the time Form's clean()
method is called, clean()
methods of the individual field would have already been executed.
That'all for now. This chapter was quite long. Nonetheless, we have learned a lot about Django forms. In the next chapter, we will learn how to render forms in templates.
Note: To checkout this version of the repository type git checkout 20a
.
Load Comments