How To Filter A Nested Serializer In Django Rest Framework?
Last Updated :
04 Oct, 2024
When building APIs with Django Rest Framework (DRF), nested serializers are commonly used to represent relationships between models. A nested serializer allows us to include data from related models within a serializer. However, there are instances where we might want to filter the data returned by the nested serializer based on certain conditions. In this article, we'll explore what nested serializers are and how to apply filtering techniques to them in Django Rest Framework.
What is a Nested Serializer?
A nested serializer allows us to represent complex relationships between models by embedding one serializer within another. For instance, if we have a Post model related to a Comment model, a nested serializer can include the Comment data within the Post serializer.
Example:
Python
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
class Comment(models.Model):
post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
Here, the Comment model has a foreign key to the Post model, indicating a one-to-many relationship. Using a nested serializer, we can display comments alongside posts
Filter a Nested Serializer in Django Rest Framework
Step 1: Create a New Django Project
First, create a new Django project and app:
django-admin startproject blog
cd blog
python manage.py startapp posts
Add 'posts' and 'rest_framework' to the INSTALLED_APPS in settings.py.
Python
# ...
INSTALLED_APPS = [
# ...
'posts',
'rest_framework'
]
Step 2: Define Models
We will use the Post and Comment models from above. Add them to posts/models.py:
Python
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
class Comment(models.Model):
post = models.ForeignKey(
Post, related_name='comments', on_delete=models.CASCADE)
text = models.TextField()
is_approved = models.BooleanField(default=False)
Run Migrations
python manage.py makemigrations
python manage.py migrate
Step 3: Create Serializers
We’ll create serializers to handle Post and Comment models. The PostSerializer will nest the CommentSerializer.
posts/serializers.py
Python
from rest_framework import serializers
from .models import Post, Comment
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ['text', 'is_approved']
class PostSerializer(serializers.ModelSerializer):
comments = CommentSerializer(many=True)
class Meta:
model = Post
fields = ['title', 'content', 'comments']
Step 4: Adding Data to the Database
To populate the database, use the following steps to add some Post and Comment data.
Open the Django shell:
python manage.py shell
Create some Post instances and Add Comment instances related to the Post instances.
Python
from posts.models import Post, Comment
post1 = Post.objects.create(title="Post 1", content="Content for Post 1")
post2 = Post.objects.create(title="Post 2", content="Content for Post 2")
post3 = Post.objects.create(title="Post 3", content="Content for Post 3")
# Comments for Post 1
Comment.objects.create(post=post1, text="Comment 1 on Post 1", is_approved=True)
Comment.objects.create(post=post1, text="Comment 2 on Post 1", is_approved=False)
# Comments for Post 2
Comment.objects.create(post=post2, text="Comment 1 on Post 2", is_approved=True)
Comment.objects.create(post=post2, text="Comment 2 on Post 2", is_approved=True)
# Comments for Post 3
Comment.objects.create(post=post3, text="Comment 1 on Post 3", is_approved=False)
Comment.objects.create(post=post3, text="Comment 2 on Post 3", is_approved=True)
Now, we have three Post instances, each with two Comment instances (some approved and some not approved).
Create Comment and PostApply Filtering to the Nested Serializer
Now, let’s apply filtering to the comments field inside the PostSerializer. Instead of filtering by date, we will filter comments based on whether they are approved (is_approved=True).
1. Custom Method on the Serializer
One way to filter nested data is by overriding the to_representation method of the parent serializer and applying the filter logic within that method.
posts/serializers.py
Python
class PostSerializer(serializers.ModelSerializer):
comments = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ['title', 'content', 'comments']
def get_comments(self, obj):
# Filter only approved comments
approved_comments = obj.comments.filter(is_approved=True)
return CommentSerializer(approved_comments, many=True).data
In this method, we use a custom method to filter the nested serializer: The PostSerializer has the filtering logic in the get_comments method, which filters approved comments only. No additional logic is needed in the view for this method.
posts/views.py
Python
from rest_framework import generics
from .models import Post
from .serializers import PostSerializer
class PostListView(generics.ListAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
blog/urls.py
Python
from django.contrib import admin
from django.urls import path
from posts.views import PostListView
urlpatterns = [
path('admin/', admin.site.urls),
path('posts/', PostListView.as_view(), name='post-list'),
]
Run the Server
python manage.py runserver
- URL: http://127.0.0.1:8000/posts/
- This will return only approved comments for each post.
Filter nested data in Serializer in Django Rest Framework2. Pass Filtered QuerySet in the View
Another approach is to filter the comments in the view and pass the filtered queryset to the serializer. Instead of trying to assign a filtered queryset to post.comments, we can handle the filtering during serialization and use prefetch_related() to optimize the query. Here, we filter the nested serializer directly in the view before passing it to the serializer:
Python
# posts/views.py
from rest_framework import generics
from .models import Post
from .serializers import PostSerializer
from django.db.models import Prefetch
from .models import Comment
class FilteredPostListView(generics.ListAPIView):
serializer_class = PostSerializer
def get_queryset(self):
# Use prefetch_related to fetch comments with the filter applied
return Post.objects.prefetch_related(
Prefetch('comments', queryset=Comment.objects.filter(is_approved=True))
)
urls.py
Python
from django.contrib import admin
from django.urls import path
from posts.views import FilteredPostListView
urlpatterns = [
path('admin/', admin.site.urls),
path('posts-filtered/', FilteredPostListView.as_view(), name='filtered-post-list'),
]
Run the Server
python manage.py runserver
- URL: http://127.0.0.1:8000/posts-filtered/
- This will also return only approved comments for each post, with filtering happening in the view.
Filtering Nested Serializer in Django Rest Framework
3. Using DRF Filters for Nested Data:
If we want to add more flexibility to our filtering, we can use query parameters along with Django’s filtering capabilities. First, install the django-filter package:
pip install django-filter
Update the settings to enable filtering:
settings.py
Python
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
In views.py, we can now use filter backends to dynamically filter data based on query parameters. We'll enable dynamic filtering using query parameters for flexibility:
posts/views.py
Python
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import generics
from .models import Post
from .serializers import PostSerializer
class PostListFilterView(generics.ListAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
filter_backends = [DjangoFilterBackend]
filterset_fields = {
'comments__is_approved': ['exact']
}
blog/urls.py
Python
from django.contrib import admin
from django.urls import path
from posts.views import PostListFilterView
urlpatterns = [
path('admin/', admin.site.urls),
path('posts-dynamic-filter/', PostListFilterView.as_view(), name='post-list-filter'),
]
Run the Server
python manage.py runserver
- URL: http://127.0.0.1:8000/posts-dynamic-filter/?comments__is_approved=true
- This will dynamically filter posts based on the approved status of comments.
Filtering using django-filtersConclusion
Each method provides a different way to filter nested serializers in Django Rest Framework. Depending on our needs, we can choose to filter directly in the serializer, within the view, or using query parameters via Django's filtering system.
Similar Reads
Python Tutorial - Learn Python Programming Language Python is one of the most popular programming languages. Itâs simple to use, packed with features and supported by a wide range of libraries and frameworks. Its clean syntax makes it beginner-friendly. It'sA high-level language, used in web development, data science, automation, AI and more.Known fo
10 min read
Python Interview Questions and Answers Python is the most used language in top companies such as Intel, IBM, NASA, Pixar, Netflix, Facebook, JP Morgan Chase, Spotify and many more because of its simplicity and powerful libraries. To crack their Online Assessment and Interview Rounds as a Python developer, we need to master important Pyth
15+ min read
Non-linear Components In electrical circuits, Non-linear Components are electronic devices that need an external power source to operate actively. Non-Linear Components are those that are changed with respect to the voltage and current. Elements that do not follow ohm's law are called Non-linear Components. Non-linear Co
11 min read
Python OOPs Concepts Object Oriented Programming is a fundamental concept in Python, empowering developers to build modular, maintainable, and scalable applications. By understanding the core OOP principles (classes, objects, inheritance, encapsulation, polymorphism, and abstraction), programmers can leverage the full p
11 min read
Python Projects - Beginner to Advanced Python is one of the most popular programming languages due to its simplicity, versatility, and supportive community. Whether youâre a beginner eager to learn the basics or an experienced programmer looking to challenge your skills, there are countless Python projects to help you grow.Hereâs a list
10 min read
Python Exercise with Practice Questions and Solutions Python Exercise for Beginner: Practice makes perfect in everything, and this is especially true when learning Python. If you're a beginner, regularly practicing Python exercises will build your confidence and sharpen your skills. To help you improve, try these Python exercises with solutions to test
9 min read
Python Programs Practice with Python program examples is always a good choice to scale up your logical understanding and programming skills and this article will provide you with the best sets of Python code examples.The below Python section contains a wide collection of Python programming examples. These Python co
11 min read
Spring Boot Tutorial Spring Boot is a Java framework that makes it easier to create and run Java applications. It simplifies the configuration and setup process, allowing developers to focus more on writing code for their applications. This Spring Boot Tutorial is a comprehensive guide that covers both basic and advance
10 min read
Python Introduction Python was created by Guido van Rossum in 1991 and further developed by the Python Software Foundation. It was designed with focus on code readability and its syntax allows us to express concepts in fewer lines of code.Key Features of PythonPythonâs simple and readable syntax makes it beginner-frien
3 min read
Python Data Types Python Data types are the classification or categorization of data items. It represents the kind of value that tells what operations can be performed on a particular data. Since everything is an object in Python programming, Python data types are classes and variables are instances (objects) of thes
9 min read