Advanced Guide to Nested Serializers in Django Rest Framework

Last updated 2 weeks ago | 78 views 75     5

Tags:- Python Django DRF

Nested serializers allow you to represent relationships between models in a structured, human-readable way. While basic nesting is simple, advanced nested serialization—especially writable nested serializers—can be more complex.

In this article, we’ll cover:

  • ✅ Recap of basic nested serializers

  • Writable nested serializers

  • Handling nested create and update

  • Using SerializerMethodField for read-only custom nesting

  • Performance tips

  • ⚠️ Common pitfalls

  • Full example: Orders with nested OrderItems


What Are Nested Serializers?

A nested serializer means a serializer is included as a field inside another serializer, typically to represent relationships like ForeignKey or OneToMany.

Example:

class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = ['id', 'name']

class BookSerializer(serializers.ModelSerializer):
    author = AuthorSerializer()
    class Meta:
        model = Book
        fields = ['title', 'author']

This gives you:

{
  "title": "My Book",
  "author": {
    "id": 1,
    "name": "John Doe"
  }
}

But what if you want to write nested data (e.g. create/update a book and its author together)? That’s where things get advanced.


Writable Nested Serializers

DRF does not support writable nested serializers out of the box, but you can implement this manually using create() and update() methods.


Example Use Case: Order and OrderItems

Models:

# models.py
class Order(models.Model):
    customer_name = models.CharField(max_length=100)

class OrderItem(models.Model):
    order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE)
    product_name = models.CharField(max_length=100)
    quantity = models.PositiveIntegerField()

Serializers:

# serializers.py
from rest_framework import serializers
from .models import Order, OrderItem

class OrderItemSerializer(serializers.ModelSerializer):
    class Meta:
        model = OrderItem
        fields = ['id', 'product_name', 'quantity']

class OrderSerializer(serializers.ModelSerializer):
    items = OrderItemSerializer(many=True)

    class Meta:
        model = Order
        fields = ['id', 'customer_name', 'items']

    def create(self, validated_data):
        items_data = validated_data.pop('items')
        order = Order.objects.create(**validated_data)
        for item_data in items_data:
            OrderItem.objects.create(order=order, **item_data)
        return order

    def update(self, instance, validated_data):
        items_data = validated_data.pop('items')
        instance.customer_name = validated_data.get('customer_name', instance.customer_name)
        instance.save()

        # Clear and recreate all items (basic way)
        instance.items.all().delete()
        for item_data in items_data:
            OrderItem.objects.create(order=instance, **item_data)

        return instance

Example Request:

POST /api/orders/
{
  "customer_name": "Alice",
  "items": [
    {"product_name": "Laptop", "quantity": 1},
    {"product_name": "Mouse", "quantity": 2}
  ]
}

Using SerializerMethodField for Read-only Nesting

If you only need to read nested data (not write it), you can use SerializerMethodField.

class OrderSerializer(serializers.ModelSerializer):
    items = serializers.SerializerMethodField()

    class Meta:
        model = Order
        fields = ['id', 'customer_name', 'items']

    def get_items(self, obj):
        return OrderItemSerializer(obj.items.all(), many=True).data

⚡ Performance Tips

Tip Why
✅ Use select_related and prefetch_related Reduces N+1 queries
✅ Limit fields with fields = [...] Keeps payloads and CPU light
✅ Avoid deep nesting if possible Complex trees slow serialization
✅ Paginate nested data or flatten it Improves scalability

⚠️ Common Pitfalls

Pitfall Fix
❌ “This field is read-only” errors Define create() and update() correctly
❌ Recreating all child objects on update Implement diff logic or use third-party libs like drf-writable-nested
❌ Poor performance on large nested queries Use .only(), .select_related(), and .prefetch_related()
❌ Trying to mix ModelSerializer with overly custom logic Switch to Serializer when needed

Bonus: Use drf-writable-nested for Complex Nested Updates

For more advanced use cases, try the drf-writable-nested package, which handles nested create/update/delete automatically.

pip install drf-writable-nested
from drf_writable_nested.serializers import WritableNestedModelSerializer

class OrderSerializer(WritableNestedModelSerializer):
    items = OrderItemSerializer(many=True)

    class Meta:
        model = Order
        fields = ['id', 'customer_name', 'items']

✅ Summary

Concept Use Case
Nested Serializer Show related data inline
Writable Nested Serializer Create/update parent and child models at once
SerializerMethodField Custom read-only nested logic
drf-writable-nested Handle complex create/update for nested data

Conclusion

Nested serializers are powerful for working with relational data in Django Rest Framework. While reading nested data is straightforward, writing it requires custom logic in create() and update().

With thoughtful design and performance considerations, nested serializers help create clean, intuitive, and maintainable APIs—especially when dealing with complex data structures like orders, invoices, or user profiles.