Hello! Welcome back to our course on Django.
In the last lesson, we focused on using serializers as gatekeepers for our database. You learned how to validate incoming data for create and update operations using is_valid() and how to persist that data with .save(). So far, all our work has been with single, isolated models.
However, real-world applications are built on relationships between data. An e-commerce order has many items; a blog post has an author and multiple comments. Today, we'll bridge that gap. The learning outcome for this lesson is to implement nested serializers to represent related model data in API responses. You will learn how to structure your API output to reflect the one-to-many and many-to-many relationships in your Django models, creating rich, hierarchical JSON that is far more useful for a frontend client.
From Primary Keys to Nested Objects
By default, when a ModelSerializer encounters a relational field (like a ForeignKey), it represents it using its primary key.
{
"id": 1,
"title": "My First Post",
"content": "...",
"author": 1 // Just the author's ID
}
This is often insufficient. A client application would then have to make a separate API call to /api/authors/1/ to get the author's details. Nested serializers allow us to embed the related object's data directly within the parent object's response, saving network requests and simplifying frontend logic.
{
"id": 1,
"title": "My First Post",
"content": "...",
"author": { // The full author object is nested
"name": "Jane Doe",
"email": "jane@example.com"
}
}
Let's start by looking at a clear, code-driven article that lays out the basic structure of models and their corresponding nested serializers.
How to Handle Nested Relationships in Django REST ...
This article from Python in Plain English provides an excellent example using Author, Post, and Comment models, which clearly illustrates a multi-level relationship.
Read 'Section 2. Setting Up Models for Nested Relationships' to understand the model structure. Then, read 'Section 3. Serializing Nested Relationships' to see how AuthorSerializer and CommentSerializer are nested inside PostSerializer. Pay close attention to how comments is defined with many=True because a post can have multiple comments.
As you saw in the article, the key is to define a field on the "parent" serializer that is an instance of the "child" serializer. For a to-many relationship, you add many=True.
A Practical Walkthrough: Orders and Items
Now let's walk through building a nested serializer step-by-step. We will use a realistic e-commerce example where an Order can have multiple OrderItems. This video provides a very clear demonstration.
Django REST Framework- Nested Serializers, SerializerMethodField and Serializer Relations
The BugBytes channel has a great tutorial on this topic. We'll watch it in a few segments to focus on the key concepts.
First, watch from 01:11 to 02:06 to understand the Order and OrderItem models. Then, watch from 02:06 to 05:27. This is the core of the lesson. Observe how he: Creates the OrderItemSerializer first. Creates the OrderSerializer. Nests the OrderItemSerializer inside the OrderSerializer using a field named items, making sure to include many=True and read_only=True.
A quick note on read_only=True: while DRF supports writable nested serializers, they add significant complexity. For now, we will treat nested data as read-only, which is the most common use case. This means we'll use these serializers to display related data but would use separate, non-nested endpoints to create or update the individual related objects (e.g., a dedicated /api/order-items/ endpoint).
The related_name Connection
After setting up the serializers in the video, you might notice a common "gotcha": the nested data doesn't appear in the API response. This happens when DRF doesn't know how to access the set of related objects from the parent model instance.
The solution lies in the Django model's ForeignKey definition, specifically the related_name argument. This argument provides a name for the reverse relation, which DRF can then use to find the data.
Let's see how this is diagnosed and fixed in the video.
Django REST Framework- Nested Serializers, SerializerMethodField and Serializer Relations
This next segment of the video addresses a very common problem when implementing nested serializers.
Watch from 07:12 to 09:21. The presenter notices the items field is missing from the response. He fixes this by adding related_name='items' to the ForeignKey on the OrderItem model. Make sure you understand why the name of the serializer field (items) must match the related_name on the model.
Advanced Representation Techniques
Full nesting is powerful, but it's not the only way to represent relationships. Depending on your API's needs, you might want deeper nesting or, conversely, a flatter structure.
1. Deeper Nesting
You can nest serializers multiple levels deep. For example, our OrderItem is related to a Product. We can nest the ProductSerializer inside the OrderItemSerializer to get a full Order -> OrderItems -> Product hierarchy.
2. Flattening Relationships with source
Sometimes, deep nesting is too verbose. You might not need the full nested product object; perhaps you only need the product's name and price. You can "flatten" the structure by pulling specific fields from a related model using the source argument.
For example: product_name = serializers.CharField(source='product.name') tells DRF to populate the product_name field by looking at the product relation on the OrderItem and grabbing its name attribute.
Let's see both techniques in action.
Django REST Framework- Nested Serializers, SerializerMethodField and Serializer Relations
This part of the video demonstrates more advanced and flexible ways to shape your API response.
First, watch from 18:52 to 20:44 to see how to implement deeper nesting by adding ProductSerializer to OrderItemSerializer. Then, watch from 20:44 to 23:04 to see the alternative: using the source argument to create flattened fields like product_name and product_price directly on the OrderItemSerializer.
Test your understanding!
You have a Post model with a ForeignKey to a User model (the author). The User model has username and email fields. In your PostSerializer, you want to include the author's username as a top-level field called author_username, without nesting the entire user object.
How would you define this field in the PostSerializer?
Show answer
class PostSerializer(serializers.ModelSerializer):
# Use the `source` argument to traverse the 'author' relationship
# and get the 'username' attribute from the related User model.
author_username = serializers.CharField(source='author.username', read_only=True)
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author_username']
Performance Implications: The N+1 Query Problem
There's a critical performance consideration with nested serializers. When you retrieve a list of objects (e.g., 100 posts), naively accessing a related field (like post.author) for each one can result in 1 query to get the posts, plus 100 additional queries to get each author. This is the "N+1 query problem," and it can severely slow down your API.
Since you have a background in programming, you'll recognize the importance of optimizing database access. Django's ORM provides the solution. By customizing the queryset in your view, you can tell Django to fetch the related data in a more efficient way.
select_related('author'): ForForeignKeyandOneToOneFieldrelationships. It performs a SQLJOINand includes the fields of the related object in theSELECTstatement.prefetch_related('comments'): ForManyToManyFieldand reverseForeignKeyrelationships. It does a separate lookup for the related objects, but does so in a single query for all parent objects, effectively "pre-filling" the cache for the relation.
The article we looked at earlier has a great section on this best practice.
How to Handle Nested Relationships in Django REST ...
Let's revisit the article to focus on performance optimization, a crucial topic for building scalable backends.
Read 'Section 5. Tips and Best Practices', focusing on tip #2: 'Use Select Related for Query Optimization'. The example queryset = Post.objects.select_related('author').prefetch_related('comments__author') is a perfect illustration of how to solve the N+1 problem for a Post that has a related author and related comments (which also have authors).
By overriding the queryset in your view, you can dramatically reduce the number of database queries and improve your API's response time.
Conclusion
Great job! You've unlocked a fundamental feature of DRF that allows you to design APIs that are both powerful and convenient for clients. You can now move beyond flat data structures and represent the rich relationships within your models.
Key Takeaways:
- You can nest a serializer within another to represent one-to-one or one-to-many relationships. Use
many=Truefor the latter. - The field name in your serializer must match the
related_nameon the model'sForeignKeyfor DRF to automatically find the reverse relationship. - You can flatten relationships using the
sourceargument (e.g.,source='related_model.field_name') to avoid overly nested and verbose JSON. - Nested serializers can cause the N+1 query problem. Always optimize your view's queryset using
select_relatedandprefetch_relatedto fetch related data efficiently.
Preview of the Next Lesson:
So far in this module, we've focused entirely on serializers—defining the shape of our data. But how do we expose this data to the world? In the next module, we'll dive into API Views. We will start by learning how to create simple function-based API views using the @api_view decorator, which will be the first step in building the actual endpoints that serve your serialized data.