Skip to content

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.

python
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 value

validate_<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:

python
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 data

WARNING

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.

python
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.

python
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.
python
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.

python
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.