Serializers#
What are Serializers?#
Convert model instances ↔ JSON. Handle validation and data transformation.
ModelSerializer (80% use case)#
# myapp/serializers.py
from rest_framework import serializers
from .models import Post, Category
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__' # All fields
# Or specify fields
# fields = ['id', 'title', 'content', 'created_at']
# Or exclude
# exclude = ['slug', 'views']
# Custom field
author_name = serializers.CharField(source='author.username', read_only=True)
# Computed field
excerpt = serializers.SerializerMethodField()
def get_excerpt(self, obj):
return obj.content[:100] + '...' if len(obj.content) > 100 else obj.content
Field Types#
class PostSerializer(serializers.ModelSerializer):
# String fields
title = serializers.CharField(max_length=200)
content = serializers.CharField(allow_blank=True)
slug = serializers.SlugField(read_only=True)
# Numeric
views = serializers.IntegerField(read_only=True)
price = serializers.DecimalField(max_digits=10, decimal_places=2)
# Boolean
is_published = serializers.BooleanField(default=False)
# Date/Time
created_at = serializers.DateTimeField(read_only=True)
published_at = serializers.DateTimeField(required=False)
# Related (ForeignKey)
author = serializers.PrimaryKeyRelatedField(read_only=True)
category = serializers.PrimaryKeyRelatedField(queryset=Category.objects.all())
# ManyToMany
tags = serializers.PrimaryKeyRelatedField(many=True, queryset=Tag.objects.all())
# Nested (see below)
# category_detail = CategorySerializer(source='category', read_only=True)
class Meta:
model = Post
fields = '__all__'
Nested Serializers#
# Simple nested (read-only)
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name', 'slug']
class PostSerializer(serializers.ModelSerializer):
category_detail = CategorySerializer(source='category', read_only=True)
class Meta:
model = Post
fields = ['id', 'title', 'category', 'category_detail']
# Writable nested
class PostSerializer(serializers.ModelSerializer):
category = CategorySerializer()
class Meta:
model = Post
fields = ['id', 'title', 'category']
def create(self, validated_data):
category_data = validated_data.pop('category')
category = Category.objects.get_or_create(**category_data)[0]
return Post.objects.create(category=category, **validated_data)
def update(self, instance, validated_data):
if 'category' in validated_data:
category_data = validated_data.pop('category')
category = Category.objects.get_or_create(**category_data)[0]
instance.category = category
return super().update(instance, validated_data)
Validation#
class PostSerializer(serializers.ModelSerializer):
title = serializers.CharField(max_length=200)
# Field-level validation
def validate_title(self, value):
if len(value) < 10:
raise serializers.ValidationError("Title must be at least 10 characters")
return value
# Object-level validation
def validate(self, data):
if data.get('is_published') and not data.get('published_at'):
raise serializers.ValidationError("Published posts must have published_at")
return data
class Meta:
model = Post
fields = '__all__'
Custom Serializers (non-model)#
class ContactSerializer(serializers.Serializer):
name = serializers.CharField(max_length=100)
email = serializers.EmailField()
message = serializers.CharField()
def create(self, validated_data):
# Handle creation logic
return Contact.objects.create(**validated_data)
def update(self, instance, validated_data):
# Handle update logic
instance.name = validated_data.get('name', instance.name)
instance.save()
return instance
Read/Write Fields#
class PostSerializer(serializers.ModelSerializer):
# Read-only
created_at = serializers.DateTimeField(read_only=True)
author_name = serializers.CharField(source='author.username', read_only=True)
# Write-only (for passwords, etc.)
password = serializers.CharField(write_only=True)
class Meta:
model = Post
fields = '__all__'
SerializerMethodField#
class PostSerializer(serializers.ModelSerializer):
# Computed field
status_label = serializers.SerializerMethodField()
comment_count = serializers.SerializerMethodField()
def get_status_label(self, obj):
return 'Published' if obj.is_published else 'Draft'
def get_comment_count(self, obj):
return obj.comments.count()
class Meta:
model = Post
fields = '__all__'
Common Patterns#
# Include related object details
class PostSerializer(serializers.ModelSerializer):
author_detail = serializers.SerializerMethodField()
def get_author_detail(self, obj):
return {
'id': obj.author.id,
'username': obj.author.username,
'email': obj.author.email
}
class Meta:
model = Post
fields = ['id', 'title', 'author', 'author_detail']
Next: Views & URLs