Django REST Framework: Nested Serializers Explained

Last updated 1 month, 2 weeks ago | 138 views 75     5

Tags:- Python Django DRF

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 in Meta for quick prototyping (but not recommended in production).

  • ✅ For large data sets, use pagination or hyperlinks instead of full nesting.

  • ✅ Use SlugRelatedField or PrimaryKeyRelatedField 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.