Getting Started with Custom Form Validation in Django
an introduction of clean(), and clean_<fieldname>() methods

Software Engineer
Search for a command to run...
an introduction of clean(), and clean_<fieldname>() methods

Software Engineer
No comments yet. Be the first to comment.
In this series, I will write about some basic core concepts of Django. I will not be building any project in this series, but I will walk you through the Django and its different concepts.
Part two of getting started with Django forms
In this article, I’ll walk through how to build a basic to-do app using event-driven architecture principles. This system is designed to handle high traffic workloads and achieve scalability. Prerequisites This article will focus on using Kafka with ...

Get familiar with user and role in PostgreSQL, and its authentication mechanism.

Part two of getting started with Django forms

Part 1: working with forms.Form

This is the third part of the "getting started with Django forms" series. Until now I have discussed
In this part of the series, I will discuss "what is form validation?", "what are the different ways of validating forms in Django", and I will wrap this part with the discussion of clean() and clean_() methods.
PS. I will be using the same project that I have created in the first part
Let's get started!
In simple words, form validation is a technique used to make sure that visitor has passed the same values expected by the backend. For example, if you have a login form with an email and a password field that authorizes visitors then you may not want to process the login form if its email field does not contain a "@".
Form validations are generally done in two different steps, also known as types of validation.
1. Client-side form validation
HTML5 comes with the ability to run validations by itself. In this way, if you have defined an email field in a form then HTML5 will validate it on the client-side before sending it to the server. Similarly, sometimes developers may write client-side form validation in JavaScript to validate the form before sending it to the server. User registration can be a use-case, in which with the help of JavaScript developer may check if the password and confirm password fields matched or not. In this way, we can say that if the form validation is run on the browser, it is said to be a client-side form validation. There is no role of the backend in the client-side form validation. Client-side form validation is good for field-level validation, but we cannot fully rely on it for security reasons.
client-side validation should not be considered an exhaustive security measure! Your apps should always perform security checks on any form-submitted data on the server-side as well as the client-side, because client-side validation is too easy to bypass, so malicious users can still easily send bad data through to your server.
2. Server-side form validation
The server-side validation is a type of validation that is run on the server once the form is submitted by the client-side. In this article, I will be discussing how Django handles server-side validation.
Before getting started with different validation techniques, it is important to understand how Django processes a form. The below flow chart explains it pretty well.

Based on the diagram above, the main things that Django's form handling does are:
Display the default form the first time it is requested by the user.
Let's look at the pictures below that describes unbounded, and bounded form



When a user first time sends the request to access the form, Django renders a blank form (in the case of the POST method) known as unbounded form Figure 1 . The unbounded form goes under client-side validation run by the browser itself when the user does not fill the form correctly Figure 2. Once the form surpasses the client-side validation, the server-side validation takes place. Now the form is bounded by session storage. If the server-side validation raises ValidationError then the form will get rerendered with pre-filled values and the error message Figure 3. Note that in case of incorrect form submission, the URL of the form remains the same but error messages are displayed in case of the server-side validation error.
We can use the is_bound attribute to know whether the form is in a bound state or not. If the form is in the bound state then the is_bound returns True, otherwise False.
When Django finds is_valid() method, it calls the clean() method clean() method calls to_python(), validate(), and run_validate() methods in the correct order and propagating their errors. If at any time, any of the methods raise ValidationError, the validation stops and that error is raised. This method returns the clean data, which is then inserted into the cleaned_data dictionary of the form.
clean_$fieldname$() method can be used to run validation for a single field of a form. $fieldname$ should be replaced by the field name, for example, if a form has an email field named "email_field" and you want to validate it then you can define clean_email_field()
A lot of theory on form validation has been covered, now let's write some code, and validate a very simple form.
Let's suppose that we have a contact form (we have already created in the first part of this series) and we want that any message sent by example@example.com should not be saved, rather tell the user to use any email except this. Add a clean method to the ContactUsForm to validate this. form_without_model/forms.py
from django import forms
from django.core.exceptions import ValidationError # import this
from django.forms.fields import EmailField
from django.forms.widgets import Textarea
class ContactUsForm(forms.Form):
name = forms.CharField(max_length=100)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
# add this
def clean_email(self):
"""
clean the email field to prevent users from using example@example.com
"""
email_address = self.cleaned_data['email'] # email is stored in cleaned_data by clean() method called by is_valid()
if email_address == 'example@example.com':
raise ValidationError("this email cannot be used, please enter another email address")
return email_address
Here, the is_valid() method present in contact_us_view at form_without_model/views.py calls the clean() method which runs field level validations such as user has entered a valid email or not and so on. Here, I did not override it. The clean() method, after validating fields return an object named cleaned_data which has key-value pair of all the form fields and their values. Till now, Django has done half of the validation, but we want to prevent users from entering example@example.com so we have to define a clean method for the email field only. Now, let's run the server to see the output of the above code. Access the page from http://127.0.0.1:8000/form-without-model/ or http://localhost:8000/form-without-model/

As expected, if a user enters example@examle.com, an error appears asking user to enter another email.
We can also validate the model forms in a similar way. Let's validate the form that we had created on PART II. Let's restrict the users from entering heading having characters less than 25.
Modify working_with_modelform/CreateBlogPostModelForm as,
from django.core.exceptions import ValidationError # imported this
from django.forms import ModelForm
from django.utils.translation import gettext_lazy as _
from .models import BlogPost
class CreateBlogPostModelForm(ModelForm):
# added content starts
def clean_title(self):
title = self.cleaned_data['title']
if len(title) < 25:
raise ValidationError("Heading must be 25 characters long!")
return title
# added content ends
class Meta:
model = BlogPost
fields = ['title', 'body']
labels = {'title': _('Heading')}
help_texts = {
'title': _('Give your blog post a suitable heading'),
'body': _('Write the content of the blog')
}
Always return a value to use as the newly cleaned data, even if the method didn't change it.
Now, let's run the server and access the page http://127.0.0.1:8000/posts/create/
I have tried creating a new blog with a heading of less than 25 characters as seen in the below image.

As expected, Django threw an error saying Heading must be 25 characters long!
Alright, that's it for this part. In the next part of the series, we will see Model validation, and how we can reuse validators.