Django Forms | Custom Form Validations
In this post we’ll learn to create user-defined functions, displaying validation errors in the template for Django Form Validations.
Table Of Contents
- Introduction
- Creating Form
- Rendering Form
- Saving Form
- Form Validation User-Defined Functions
- Conclusion
Introduction
The forms are a Django Module which deals with all form-related functions from binding POST data to form, Validating form and rendering HTML field in the template.
We’ll be using below models.py file as an example to demonstrate form validations.
from django.db import models from django.contrib.auth.models import User from datetime import datetime class AuthUserProfile(models.Model): user_profile_id = models.AutoField(primary_key=True) user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='auth_user_profile') dob = models.DateField(blank=True, null=True) is_deleted = models.PositiveSmallIntegerField(default=0) created_at = models.DateTimeField(auto_now=datetime.now(), null=True) updated_at = models.DateTimeField(auto_now=datetime.now(), null=True) class Meta(): db_table = 'auth_user_profile' verbose_name = 'User Profile' verbose_name_plural = 'User Profiles' def __str__(self): return self.user
Create a form which has these fields (first_name, last_name, username, password, email) from User
models and field (dob) in AuthUserProfile
Model and also will add custom field and non-field level validation.
Creating Form
In forms.py file import forms from Django. Inherit forms.Form
to UserForm
and add attributes to the field.
from django import forms from datetime import datetime from django.contrib.auth.models import User class UserForm(forms.Form): first_name = forms.CharField(label="First Name*",widget=forms.TextInput(attrs={'required':True,'class':"form-control"})) last_name = forms.CharField(label="Last Name*",widget=forms.TextInput(attrs={'required':True,'class':"form-control"})) username = forms.CharField(label="User Name*",widget=forms.TextInput(attrs={'required':True,'class':"form-control"})) email = forms.CharField(label="Email",widget=forms.TextInput(attrs={'type':'email','required':False,'class':"form-control"})) date_of_birth = forms.CharField(label="Date of Birth",widget=forms.TextInput(attrs={'type':'date','required':True,'class':"form-control"})) password = forms.CharField(label="Password*",widget=forms.TextInput(attrs={'required':True,'class':"form-control", 'type' : "password"})) confirm_password = forms.CharField(label="Confirm Password*",widget=forms.TextInput(attrs={'required':True,'class':"form-control", 'type' : "password"})) def clean(self): # user age must be above 18 to register if self.cleaned_data.get('date_of_birth'): dob = datetime.strptime(self.cleaned_data.get('date_of_birth'),"%Y-%m-%d") now = datetime.now() diff = now.year-dob.year if diff < 18: msg="User must be atleast 18 years old" self.add_error(None, msg) #check if user name is unique username_count = User.objects.filter(username=self.cleaned_data.get('username')).count() if username_count>0: msg="Username '{}' has already been used.".format(self.cleaned_data.get('username')) self.add_error(None, msg) def clean_confirm_password(self): password = self.cleaned_data.get('password') confirm_password = self.cleaned_data.get('confirm_password') if confirm_password!=password: msg = "Password and Confirm Passwords must match." self.add_error('confirm_password', msg)
You may notice clean()
and clean_confirm_password()
methods in UserForm
the form they are validation methods.
The clean()
the method is form level validation this can also be used to perform field-level validation.
And the clean_confirm_password()
is a field-level validation for confirm_password
the field it checks if confirm_password!=password
then adds error to a then particular field.
Rendering Form
Rendering of forms is an easy part we must pass the form object as an argument to render function.
In views.py create a function user_profile_create
which will display rendered form.
from django.contrib.auth.models import User from users.models import AuthUserProfile from forms.forms import UserForm from django.contrib.auth.hashers import make_password from django.contrib import messages def user_profile_create(request): form = UserForm() template="forms/user_profile_create_form.html" return render(request,template,{"form":form})
form = UserForm()
creates form object of UserForm and is passed as an argument to the render()
function.
In urls.py
file add routes to view.
urlpatterns = [ path('user/profile/create', views.user_profile_create, name='user-profile-create'), ]
Create an HTML file in your apps template folder naming user_profile_create_form.html.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Django Form Validation</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script> <style> .error{ color:red; } </style> </head> <body> <div class="container"> {% if messages %} {% for message in messages %} {% if message.level == DEFAULT_MESSAGE_LEVELS.SUCCESS %} <div class="alert alert-success" role="alert"> <div id="primary-notification-div"> {{ message }} </div> </div> {% endif %} {% endfor %} {% endif %} <h1>User Profile</h1> <form action="{% url 'forms:user-profile-save' %}" method="post"> {% csrf_token %} {% if form.errors %} {% for error in form.non_field_errors %} <div class="alert alert-danger"> <strong>{{ error|escape }}</strong> </div> {% endfor %} {% endif %} <div class="row"> <div class="col-md-3"> {{form.first_name.label}} {{form.first_name}} {% if form.errors.first_name %} <label for="" class="error">{{form.errors.first_name|striptags}}</label> {% endif %} </div> <div class="col-md-3"> {{form.last_name.label}} {{form.last_name}} {% if form.errors.last_name %} <label for="" class="error">{{form.errors.last_name|striptags}}</label> {% endif %} </div> <div class="col-md-3"> {{form.username.label}} {{form.username}} {% if form.errors.username %} <label for="" class="error">{{form.errors.username|striptags}}</label> {% endif %} </div> <div class="col-md-3"> {{form.email.label}} {{form.email}} {% if form.errors.email %} <label for="" class="error">{{form.errors.email|striptags}}</label> {% endif %} </div> <div class="col-md-3"> {{form.date_of_birth.label}} {{form.date_of_birth}} {% if form.errors.date_of_birth %} <label for="" class="error">{{form.errors.date_of_birth|striptags}}</label> {% endif %} </div> </div> <div class="row" style="margin-top: 25px;"> <div class="col-md-3"> {{form.password.label}} {{form.password}} {% if form.errors.password %} <label for="" class="error">{{form.errors.password|striptags}}</label> {% endif %} </div> </div> <div class="row" style="margin-top: 25px;"> <div class="col-md-3"> {{form.confirm_password.label}} {{form.confirm_password}} {% if form.errors.confirm_password %} <label for="" class="error">{{form.errors.confirm_password|striptags}}</label> {% endif %} </div> <div class="col-md-12" style="margin-top: 25px;"> <input type="submit" class="btn btn-sm btn-primary" value="submit"> </div> </div> </form> </div> </body> </html>
This is how our form will look when we go to route user/profile/create.
- The
message
displays success message oncemessages.add_message(request, messages.SUCCESS, ".....")
is called it is just like the flash message. - The
form.errors
is called on validation has failed. - The
form.non_field_errors
display errors on top of form this errors are not associated to a particular field. - The
form.errors.
displays the error of the particular field.
This is how errors are displayed in the form.
Saving Form
In urls.py file add routes to save the form.
urlpatterns = [ path('user/profile/save', views.user_profile_create, name='user-profile-save'), ]
In views.py file add function user_profile_save()
to save form data.
def user_profile_save(request): form = UserForm(request.POST) if form.is_valid(): query = { "first_name" : form.cleaned_data.get('first_name'), "last_name" : form.cleaned_data.get('last_name'), "username" : form.cleaned_data.get('username'), "password" : make_password(form.cleaned_data.get('password')), "email" : form.cleaned_data.get('email'), "is_superuser" : 0, "is_staff" : 1, "is_active" : 1, } user = User.objects.create(**query) query={ "user_id" : user.id, "dob" : form.cleaned_data.get('dob'), } AuthUserProfile.objects.create(**query) messages.add_message(request, messages.SUCCESS, "User Profile created successfully.") return HttpResponseRedirect(reverse('forms:user-profile-create')) template="forms/user_profile_create_form.html" return render(request,template,{"form":form})
The request.POST
is passed to UserForm(request.POST)
this binds the submitted data to Form Class.
The form.is_valid()
returns a Boolean value if True
then the form is clean and if False
then there may be validation error.
To view validation errors after .is_valid()
method we can print form.errors
to view validation errors.
Calling form.cleaned_data.get('')
gives use of the sanitized value to that field. Inside .is_valid()
we have called model methods to save form data.
Form Validation User-Defined functions
To defined a custom validation function in Form Class name function with prefix clean followed by underscore and field name which must be validated.
Example
def clean_first_name(self): pass #this validates field first_name def clean_username(self): pass #this validates field username
If the value of the field is not as expected that you can raise validation error or add error by mentioning field name self.add_error('field_name', "Error Message")
.
If you want to raise non-field error than set the first argument of add_error()
method None followed by the message you want to be displayed.
self.add_error(None, msg) #this creates a non-field error
Note
For More information on Django, Forms visit the official website.
Conclusion
We have come to the end of our post on Django Form Validation.
If you have any doubts or suggestions please mention in the comments section and we’ll reach you soon and we would also love to hear requests and your recommendations for new tutorials/posts.
Related Posts
- Python Django Forms | Creating, Rendering, Validating and Saving Forms
- Django – Multiple Files Validation and Uploads




