Creating Custom Model Validation In Django

3 min read

In this tutorial, we will learn how to create custom model validators using Django.

Understanding The Problem

Django models come with their own built-in validations, that we put while creating models. However, often we require further validations on some fields. Such as the title length or age can't be lesser than a particular value or the coupon code should be in all caps.

In such scenarios building, a custom model validator is the most straightforward solution.

Note: If you want validation only limited to the admin interface then read this article instead - Displaying Custom Validation Exception in Django Admin

If your goal is to create custom model validations that will remain constant throughout the app including the admin site then this tutorial is for you.

Creating Custom Model Validation In Django

We will again use the blog project for this tutorial. Our goal is to create a validator that will not allow titles with less than 10 letters.

Creating Custom Model Validation In DjangoThis is how our existing model looks.

models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200, unique=True)
    slug = models.SlugField(max_length=200, unique=True)
    author = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="blog_posts")
    status = models.IntegerField(choices=STATUS, default=0)
    content = models.TextField()


    class Meta:
        ordering = ["-created_on"]

    def __str__(self):
        return self.title

To create custom model validators we need to create a clean() method within our model class.

models.py

from django.db import models
from django.core.exceptions import ValidationError

class Post(models.Model):
    title = models.CharField(max_length=200, unique=True)
    slug = models.SlugField(max_length=200, unique=True)
    author = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="blog_posts")
    updated_on = models.DateTimeField(auto_now=True)
    content = models.TextField()
    created_on = models.DateTimeField(auto_now_add=True)
    status = models.IntegerField(choices=STATUS, default=0)
    summary = models.CharField(max_length=500, null=True, blank=True)

    class Meta:
        ordering = ["-created_on"]

    def __str__(self):
        return self.title

    def clean(self):
        if not len(self.title) > 10:
            raise ValidationError(
                {'title': "Title should have at least 10 letters"})

The clean method will raise an exception when the condition is not met.

At this point, if you save the files and went to the admin site, and try to create a post with less title less than 10 letters you would receive the error message.

Even though the admin site invokes the method. The clean method is not invoked on save() or create() by default. So the best practice is to override the save method of the model and invoke the full_clean() method that under the hood calls clean and other validation hooks.

models.py

from django.db import models
from django.core.exceptions import ValidationError

class Post(models.Model):
    title = models.CharField(max_length=200, unique=True)
    slug = models.SlugField(max_length=200, unique=True)
    author = models.ForeignKey(
        User, on_delete=models.CASCADE, related_name="blog_posts")
    updated_on = models.DateTimeField(auto_now=True)
    content = models.TextField()
    created_on = models.DateTimeField(auto_now_add=True)
    status = models.IntegerField(choices=STATUS, default=0)
    summary = models.CharField(max_length=500, null=True, blank=True)

    class Meta:
        ordering = ["-created_on"]

    def __str__(self):
        return self.title

    def clean(self):
        if not len(self.title) > 10:
            raise ValidationError(
                {'title': "Title should have at least 10 letters"})

   def save(self, *args, **kwargs):
        self.full_clean()
        return super().save(*args, **kwargs)

That's all!

Save the file, run the server and see the validators in action.


DJANGO
author's image
Abhijeet Pal Author and Editor in Chief @djangocentral

Abhijeet is a full-stack software developer from India with a strong focus on backend and system design. He is driven by the need to create impactful solutions that add value to the internet in any way possible.

LinkedIn Twitter Github

Latest Articles

Latest from djangocentral

Django 4.1 adds async-compatible interface to QuerySet

The much-awaited pull request for an async-compatible interface to Queryset just got merged into the main branch of Django.Pull Request - https://github.com/django/django/pull/14843 The Django core team has been progressively adding async suppor…
Read more →

3 min read

Making Django Admin Jazzy With django-jazzmin

Django admin is undoubtedly one of the most useful apps of Django. Over the years there has been very little change in the admin app as far as the UX is concerned and it's not a bad thing at all. Django admin was designed to provide a simple and minimali…
Read more →

4 min read