Django REST Framework Validation with Serializers
Last updated 1 month, 2 weeks ago | 134 views 75 5

Validating data is a critical part of building any API. In Django REST Framework (DRF), serializers provide built-in and customizable validation mechanisms that ensure your input data is clean, consistent, and secure before hitting the database.
In this article, you'll learn:
-
Why validation is important
-
DRF’s validation flow
-
validate_<field>()
for field-level validation -
validate()
for object-level validation -
Custom error messages
-
Real-world examples
-
Best practices and common mistakes
Why is Validation Important?
Validation helps:
-
Prevent invalid or malicious data from being saved
-
Enforce business logic
-
Improve API reliability and user experience
For example, a user shouldn’t be allowed to set a future date for a past event or enter the same value for "username" and "email".
DRF Validation Flow Overview
When you call .is_valid()
on a serializer:
-
Each field is validated individually (
validate_<field>()
) -
The entire object is validated (
validate()
) -
If everything is valid,
.save()
creates or updates the model
Field-Level Validation with validate_<field>()
Use this method to add logic for a specific field:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['title', 'author', 'published']
def validate_title(self, value):
if "test" in value.lower():
raise serializers.ValidationError("Title cannot contain 'test'.")
return value
-
value
is the incoming data for the field. -
You must
return value
if it’s valid.
Object-Level Validation with validate(self, data)
Use this to validate multiple fields together:
def validate(self, data):
if data['title'] == data['author']:
raise serializers.ValidationError("Title and author cannot be the same.")
return data
This is ideal when validation depends on a combination of fields.
✍️ Custom Error Messages
You can customize error messages in two ways:
1. Inside the serializer field definition:
title = serializers.CharField(
max_length=100,
error_messages={'blank': 'Title is required.', 'max_length': 'Title is too long.'}
)
2. In the validation methods:
raise serializers.ValidationError("Custom error message here.")
Example: User Registration Serializer
class RegisterSerializer(serializers.Serializer):
username = serializers.CharField()
email = serializers.EmailField()
password = serializers.CharField(write_only=True)
password2 = serializers.CharField(write_only=True)
def validate_username(self, value):
if User.objects.filter(username=value).exists():
raise serializers.ValidationError("Username is already taken.")
return value
def validate(self, data):
if data['password'] != data['password2']:
raise serializers.ValidationError("Passwords do not match.")
return data
Common Pitfalls
Problem | Solution |
---|---|
Returning nothing from validate() |
Always return data |
Not using write_only=True for passwords |
Use write_only=True to avoid exposing sensitive fields |
Using clean() from Django forms in DRF |
Use validate() instead |
Putting object-level logic in field-level validators | Use validate() for multi-field checks |
✅ Best Practices
-
✅ Use
validate_<field>()
for individual field checks -
✅ Use
validate()
for logic that spans fields -
✅ Return cleaned
value
ordata
from each validator -
✅ Keep error messages user-friendly
-
✅ Reuse validation functions to avoid duplication
Full Example: Blog Post Validator
from rest_framework import serializers
from .models import BlogPost
class BlogPostSerializer(serializers.ModelSerializer):
class Meta:
model = BlogPost
fields = ['title', 'content', 'author']
def validate_title(self, value):
if len(value) < 10:
raise serializers.ValidationError("Title must be at least 10 characters long.")
return value
def validate(self, data):
if 'django' in data['content'].lower() and not data['title'].lower().startswith('django'):
raise serializers.ValidationError("Posts about Django must start the title with 'Django'.")
return data
Final Thoughts
Proper validation is essential in building robust APIs. DRF's ModelSerializer
makes it easy to enforce field-level and object-level validation using validate_<field>()
and validate()
methods.
With well-crafted validation logic, your API will be secure, user-friendly, and resilient against bad input.