Django REST Framework: Nested Serializers Explained
Last updated 1 month, 2 weeks ago | 138 views 75 5

When building APIs, it’s common to work with related models — for example, a blog post and its comments, or an order and its items. To represent these relationships properly in your API responses and requests, Django REST Framework (DRF) offers a powerful feature: Nested Serializers.
What Are Nested Serializers?
Nested serializers allow you to include one serializer within another to represent relationships between models. This is useful when:
-
You want to display related model data in an API response
-
You want to accept related model data in API input (e.g., create nested objects)
Use Case: Author and Book Models
Let’s work with two related models:
# models.py
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, related_name='books', on_delete=models.CASCADE)
Each Book
has an Author
, and each Author
can have many Books
.
✅ Step 1: Create Basic Serializers
# serializers.py
from rest_framework import serializers
from .models import Author, Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['id', 'title', 'author']
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ['id', 'name']
This setup shows foreign key IDs by default. To include full author details in the book, we’ll nest the serializer.
Step 2: Read-Only Nested Serializer
class AuthorSerializer(serializers.ModelSerializer):
books = BookSerializer(many=True, read_only=True)
class Meta:
model = Author
fields = ['id', 'name', 'books']
Now, when you GET an author, you get all their books too:
{
"id": 1,
"name": "J.K. Rowling",
"books": [
{ "id": 1, "title": "Harry Potter 1", "author": 1 },
{ "id": 2, "title": "Harry Potter 2", "author": 1 }
]
}
✍️ Step 3: Writeable Nested Serializers (Create Nested Objects)
By default, nested serializers are read-only. If you want to create or update nested objects, override the create()
or update()
method.
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['title']
class AuthorSerializer(serializers.ModelSerializer):
books = BookSerializer(many=True)
class Meta:
model = Author
fields = ['name', 'books']
def create(self, validated_data):
books_data = validated_data.pop('books')
author = Author.objects.create(**validated_data)
for book_data in books_data:
Book.objects.create(author=author, **book_data)
return author
Input JSON:
{
"name": "George R. R. Martin",
"books": [
{"title": "A Game of Thrones"},
{"title": "A Clash of Kings"}
]
}
Result:
This creates an Author
and two related Book
objects in a single API call.
Nested Updates
To support updates, override update()
similarly. Be cautious — DRF does not handle nested updates automatically.
Tips for Performance and Simplicity
-
✅ Use nested serializers only when needed — flatten for performance.
-
✅ Use
depth = 1
inMeta
for quick prototyping (but not recommended in production). -
✅ For large data sets, use pagination or hyperlinks instead of full nesting.
-
✅ Use
SlugRelatedField
orPrimaryKeyRelatedField
for simple relationships.
⚠️ Common Pitfalls
Problem | Solution |
---|---|
Nested writes don’t work | Override create() and update() |
Slowness with large nested queries | Avoid deep nesting; use select_related and prefetch_related |
Validation issues with nested inputs | Carefully handle field validations and check types |
Using depth too heavily |
Use explicit nested serializers for clarity and control |
Complete Working Example
models.py
class Author(models.Model):
name = models.CharField(max_length=100)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(Author, related_name='books', on_delete=models.CASCADE)
serializers.py
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ['title']
class AuthorSerializer(serializers.ModelSerializer):
books = BookSerializer(many=True)
class Meta:
model = Author
fields = ['name', 'books']
def create(self, validated_data):
books_data = validated_data.pop('books')
author = Author.objects.create(**validated_data)
for book_data in books_data:
Book.objects.create(author=author, **book_data)
return author
views.py
from rest_framework import viewsets
from .models import Author
from .serializers import AuthorSerializer
class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
Final Thoughts
Nested serializers are powerful tools for representing complex relationships in DRF. While they improve clarity and usability for clients, they require careful management for performance and correctness.
Stick to read-only nesting where possible, and use custom create/update logic only when necessary.