Django REST Framework Serializer Validation
Implement multi-layer validation in DRF serializers for robust API input handling and security.
Overview
Django REST Framework serializers provide a powerful validation system that operates at multiple layers, from field-level validation to object-level validation and even cross-field validation. Understanding this multi-layer approach is essential for building secure APIs that properly handle malicious input. Field-level validation is implemented by adding validate_<field_name> methods to serializer classes. These methods receive the field value and can either return a cleaned value or raise ValidationError. This is the first line of defense, ensuring individual fields meet format and constraint requirements before any object-level processing occurs. Object-level validation in the validate() method operates on the complete validated data after individual field validation has passed. This enables complex business logic that depends on relationships between fields. Common patterns include checking that a end_date is after start_date, verifying user permissions for certain actions, or ensuring uniqueness constraints across related fields. The create() and update() methods in ModelSerializer classes handle the actual persistence of validated data. These methods receive already-validated data, ensuring that only properly validated information reaches the database layer. The validated_data dictionary contains only fields that passed all validation, eliminating the risk of unexpected fields being persisted. DRF's validation system also integrates with Django's permission and authentication classes. The request user is available in serializer context, enabling validation rules that depend on user permissions or roles. For example, a serializer might allow certain field values only for admin users while enforcing stricter limits for regular users.
Code Example
from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.utils import timezone
from .models import Project, Task
User = get_user_model()
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Task
fields = ['id', 'title', 'description', 'status', 'assignee', 'due_date', 'project']
read_only_fields = ['id']
def validate_title(self, value):
if len(value) < 3:
raise serializers.ValidationError("Title must be at least 3 characters.")
if len(value) > 200:
raise serializers.ValidationError("Title cannot exceed 200 characters.")
return value.strip()
def validate_due_date(self, value):
if value and value < timezone.now().date():
raise serializers.ValidationError("Due date cannot be in the past.")
return value
def validate(self, attrs):
if attrs.get('status') == 'completed' and not attrs.get('assignee'):
raise serializers.ValidationError({
'assignee': 'Completed tasks must have an assignee.'
})
project = attrs.get('project')
if project and project.status == 'archived':
raise serializers.ValidationError({
'project': 'Cannot add tasks to archived projects.'
})
return attrs
def create(self, validated_data):
validated_data['created_by'] = self.context['request'].user
return super().create(validated_data)
class ProjectSerializer(serializers.ModelSerializer):
tasks = TaskSerializer(many=True, read_only=True)
task_count = serializers.SerializerMethodField()
completion_rate = serializers.SerializerMethodField()
class Meta:
model = Project
fields = [
'id', 'name', 'description', 'status', 'owner',
'created_at', 'updated_at', 'tasks', 'task_count', 'completion_rate'
]
read_only_fields = ['id', 'created_at', 'updated_at', 'owner']
def get_task_count(self, obj):
return obj.tasks.count()
def get_completion_rate(self, obj):
total = obj.tasks.count()
if total == 0:
return 0
completed = obj.tasks.filter(status='completed').count()
return round((completed / total) * 100, 2)More Django Rules
Django QuerySet Optimization
Use select_related, prefetch_related, and only() to minimize database queries.
from django.db.models import Prefetch, Count, Q
from django.shortcuts import render
from .models import Author, Book, Publisher
# Problematic: N+1 qu...Django Security Best Practices
Implement comprehensive security measures including CSRF, XSS prevention, and SQL injection protection.
from django.http import HttpResponse, HttpResponseForbidden
from django.views.decorators.http import require_http_methods
from django.views.decorators...Django REST Framework Permissions
Implement granular permission systems with DRF permission classes and custom permission logic.
from rest_framework import permissions
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.gener...