Validation
Data validation in DRF happens in several layers: field-level validation, object-level validation, and explicit reusable validators.
Serializer Validation Methods
You can add custom validation directly to your serializers using validate_<field_name>() (field-level) or validate() (object-level) methods.
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ['title', 'body']
def validate_title(self, value):
if 'django' not in value.lower():
raise serializers.ValidationError("Article must be about Django")
return valuevalidate_<field_name>() receives a single value. validate() receives the full dictionary of validated data, which is where rules that compare fields against each other belong:
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = Event
fields = ['name', 'start_date', 'end_date']
def validate(self, data):
if data['start_date'] > data['end_date']:
raise serializers.ValidationError("End date must come after start date")
return dataWARNING
Both kinds of method must return the value they validate. Forgetting the return silently sets the field, or the entire object, to None. Note also that on a PATCH request fields may be absent, so use data.get('start_date') rather than indexing when your serializer supports partial updates.
Validators
Validators are useful for removing validation logic from your serializers and view classes, making them reusable across multiple classes.
Most of the time you're dealing with validation in REST framework you'll simply be writing default field validation, or falling back to Django's existing validators. However, you can write custom validators if you need to.
Function-based Validators
A validator can be any callable that raises a serializers.ValidationError on failure.
from rest_framework import serializers
def multiple_of_ten(value):
if value % 10 != 0:
raise serializers.ValidationError('Not a multiple of ten')
class GameRecord(serializers.Serializer):
score = serializers.IntegerField(validators=[multiple_of_ten])Class-based Validators
You can also write class-based validators by defining a __call__ method. This allows you to pass parameters to your validator.
from rest_framework import serializers
class MultipleOf:
def __init__(self, base):
self.base = base
def __call__(self, value):
if value % self.base != 0:
message = f'This field must be a multiple of {self.base}.'
raise serializers.ValidationError(message)
class GameRecord(serializers.Serializer):
score = serializers.IntegerField(validators=[MultipleOf(base=10)])Built-in Validators
DRF includes a few built-in validators, primarily for uniqueness constraints:
UniqueValidator: Asserts that a single field is unique across the queryset.UniqueTogetherValidator: Asserts that two or more fields are unique together.UniqueForDateValidator,UniqueForMonthValidator,UniqueForYearValidator: Validates that a field is unique for a given date field.
from rest_framework.validators import UniqueValidator
from rest_framework import serializers
from .models import BlogPost
class BlogPostSerializer(serializers.Serializer):
title = serializers.CharField(
max_length=100,
validators=[UniqueValidator(queryset=BlogPost.objects.all())]
)NOTE
ModelSerializer adds these automatically: unique=True on a model field becomes a UniqueValidator, and unique_together becomes a UniqueTogetherValidator. Declaring one by hand on top of a generated one produces duplicate error messages. UniqueTogetherValidator also requires every field it covers to be present in the payload, which is a common cause of unexpected PATCH failures.
TIP
For a complete and exhaustive list of built-in validators, refer to the official DRF documentation. Additionally, you can reuse any of Django's built-in core validators (such as EmailValidator, RegexValidator, MinValueValidator, etc.) directly in your DRF serializers!
Calling is_valid() in Views
Before you can access the validated data or save an object instance, you must call is_valid() on the serializer. This method returns True if the data successfully passes all validation rules, and False if it fails. Calling is_valid() triggers all the field-level validate_<field_name>() methods, the object-level validate() method, as well as any explicit validators attached to the fields.
If validation fails, the .errors attribute will contain a dictionary representing the error messages.
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['POST'])
def create_article(request):
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid():
# Data is valid, you can save it or access it
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
# Validation failed, return the errors
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)TIP
You can pass raise_exception=True to is_valid() (e.g., serializer.is_valid(raise_exception=True)). If validation fails, DRF will automatically raise a ValidationError which will return a standard 400 Bad Request response, saving you from writing the if/else block.
