Form validation is an important part of every web application. It enables you to verify that the data entered by the user is correct and satisfies the requirements of your application.
Django forms are robust, powerful, versatile, and expandable. The ability to combine forms, models, and views enables us to complete a lot of work with little effort.
In this tutorial, we will learn.
1. Model Form with default validators.
2. How to add custom form field validators.
3. Overriding the clean() function for validation.
4. One Form tip and trick.
Let's create a Product
model.
# models.py
from django.db import models
class Product(models.Model):
title = models.CharField(null=False, blank=False, max_length=25)
slug = models.CharField(unique=True, max_length=20)
quantity = models.PositiveIntegerField()
price = models.DecimalField(max_digits=5, decimal_places=2)
def __str__(self):
return self.title
Let's create a ModelForm
"ProductForm
" now.
# forms.py
from django import forms
from .models import Product
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = '__all__'
This code defines a ProductForm
class that subclasses Django's ModelForm
class. ModelForm
is a useful class that allows you to create forms from Django models. This means that the ProductForm
class will have a form field for each of the model fields defined in the Product
model.
Finally, in the Meta
class, we specify that the ProductForm
should use the Product
model and include all fields from the model. This will create form fields for each field in the Product
model.
Now we will write a code to display our form.
# views.py
from django.shortcuts import render
from django.views.generic import CreateView
from .models import Product
from .forms import ProductForm
class ProductCreate(CreateView):
model = Product
form_class = ProductForm
template_name = 'products/create_product.html'
#urls.py
from django.urls import path
from .views import ProductCreate
urlpatterns = [
path('create/', ProductCreate.as_view(), name="create_product")
]
Now let's write a code in create_product.html
<!-- create_product.html -->
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
Go to your URL and you will see the following:
1. Model Form with default validators.
We have a ProductForm which is a ModelForm. And in our model form, we are using all fields of our model.
All ModelForm rely on the default field validation rules of our model.
For example: In our price field we have specified max_digits=5. So if we put more than 5 digits then it should throw the validation error.
price = models.DecimalField(max_digits=5, decimal_places=2)
Although Django provides us with a tonne of fantastic defaults or built-in form validation like MaxLengthValidator, MinLengthValidator
, in actuality, the defaults are never sufficient. We are aware of this, hence we learn how to create custom validators in the next step.
2. How to add custom form field validators.
Here we are applying the validtor on field level. Let's say you want that every title of a product should start with "Amazing". like Amazing Shoes, Amazing Joggers, etc.
We can solve this problem with a simple custom validator. Create a new file validators.py in your app.
# validators.py
from django.core.exceptions import ValidationError
def validate_amazing(value):
"""Raise a ValidationError if the value doesn't start with the
word 'Amazing'.
"""
if not value.startswith('Amazing'):
msg = 'Must start with Amazing'
raise ValidationError(msg)
The validate_amazing
function is a custom validator that raises a ValidationError
if the given value doesn't start with the word 'Amazing'. This validator can be used to ensure that the title
field of a Product
always starts with the word 'Amazing'.
We can add this validator in both models or forms according to our requirements.
a. Adding validate_amazing in models.py
Here we are adding the validate_amazing
validator in the title field by specifying validators=[validate_amazing].
# models.py
from django.db import models
from .validators import validate_amazing
class Product(models.Model):
# new
title = models.CharField(
null=False, blank=False, max_length=25, validators=[validate_amazing]
)
slug = models.CharField(unique=True, max_length=20)
quantity = models.PositiveIntegerField()
price = models.DecimalField(max_digits=5, decimal_places=2)
def __str__(self):
return self.title
b. Adding validate_amazing in forms.py
Now we will learn how to add validators in forms.
# forms.py
from django import forms
from .models import Product
from .validators import validate_amazing
class ProductForm(forms.ModelForm):
# new
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].validators.append(validate_amazing)
class Meta:
model = Product
fields = '__all__'
In the __init__
method of the ProductForm
class, we are appending a custom validator called validate_amazing
to the list of validators for the title
field.
3. Overriding the clean() function for validation.
After the default and custom field validators are run, Django provides the second stage of validation through the clean()
method and clean_<field_name>()
methods.
a. clean(
): Since the clean() method isn't exclusive to any one particular field, it is used to validate two or more fields against one another.
b. clean_<field_name>():
The clean_<field_name>()
methods are similar to the clean()
method, but are specific to individual fields on the form. These methods are called after the clean()
method and allow you to perform custom validation on individual form fields.
Let's explore each one by one.
a. clean().
# forms.py
from django import forms
from .models import Product
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = '__all__'
def clean(self):
cleaned_data = super().clean()
slug = cleaned_data.get('slug', '')
title = cleaned_data.get('title', '')
# slug and title should be same example
if slug != title.lower():
msg = "slug and title should be same"
raise forms.ValidationError(msg)
return cleaned_data
cleaned_data
variable is a dictionary that contains the cleaned and validated data from the form. The code then retrieves the slug
and title
fields from the cleaned_data
dictionary.
Then it compares the value of the slug
field with the lowercase version of the title
field. If the slug
and the lowercase title
are not the same, the code raises a validation error with the message "slug and title should be same". This error message will be displayed to the user if the form fails validation. Finally, the clean
method returns the cleaned_data
dictionary, which contains the cleaned and validated data from the form.
b. clean_<field_name>()
# forms.py
from django import forms
from .models import Product
from .validators import validate_amazing
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = '__all__'
def clean_quantity(self):
quantity = self.cleaned_data['quantity']
if quantity > 100:
msg = 'Quantity should be less than 100'
raise forms.ValidationError(msg)
return quantity
This method first retrieves the value of the quantity
field from the form's cleaned data, which is a dictionary-like object containing the form data that has been validated.
Next, the method checks if the quantity
value is greater than 100. If it is, then the method raises a ValidationError
with the message "Quantity should be less than 100". This error will be displayed to the user, informing them that their input is invalid and must be corrected.
If the quantity
value is less than or equal to 100, then the method simply returns the value of quantity
, indicating that it is valid.
4. One form tip and trick.
If you want to specify required in your form field. Then don't do this. This is a repeated duplicate code.
# forms.py
# Don't do this
from django import forms
from .models import Product
class ProductForm(forms.ModelForm):
# Don't do this - This is a duplication of the model field!
title = forms.CharField(required=True)
# Don't do this - This is a duplicationof the model field!
price = forms.DecimalField(required=True)
class Meta:
model = Product
fields = '__all__'
The ProductForm
class defines title
and price
fields that are duplicates of the title
and price
fields that already exist in the Product
model. This is unnecessary and can lead to confusion and errors.
Let me show you the correct way.
# forms.py
# Correct way
from django import forms
from .models import Product
class ProductForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['title'].required = True
self.fields['price'].required = True
class Meta:
model = Product
fields = '__all__'
The ProductForm
class overrides the __init__
method of the ModelForm
class. This method is called when an instance of the ProductForm
class is created. The __init__
method first calls the __init__
method of the parent ModelForm
class using super()
, which allows the parent class to initialize itself.
Next, the __init__
method sets the required
attribute of the title
and price
fields to True
. This means that when the form is rendered, these fields will be marked as required and the user will have to enter a value for them in order to submit the form.
That's it. If you find it useful then show some love and support by clapping or by buying me coffee. And don't forget to share it with your friends. In case of any problem, you can comment here or you can also directly approach me through my LinkedIn.
Related Articles.
How to add class and other attributes to django form fields
How to add date input widget in django forms
I would greatly appreciate it if you subscribe to my YouTube channel, Let's Code More