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
How to Change Field Name in Django REST Framework Serializer
When working with Django REST Framework (DRF), we may encounter situations where we need to change the name of a field in a serializer. This can be useful when we want to expose a model field with a different name in our API or when we need to conform to a specific API schema. In this article, we wi
3 min read
Nestest Serializer in Django Framework
In web development, creating and consuming APIs (Application Programming Interfaces) is commonplace. Django Rest Framework (DRF) serves as a robust toolkit for building APIs in Django-based web applications. Within DRF, a pivotal concept is serializers. In this article, we will delve into the concep
4 min read
ListField in serializers - Django REST Framework
In Django REST Framework the very concept of Serializing is to convert DB data to a datatype that can be used by javascript. Every serializer comes with some fields (entries) which are going to be processed. For example if you have a class with name Employee and its fields as Employee_id, Employee_n
4 min read
Numeric fields in serializers - Django REST Framework
In Django REST Framework the very concept of Serializing is to convert DB data to a datatype that can be used by javascript. Every serializer comes with some fields (entries) which are going to be processed. For example if you have a class with name Employee and its fields as Employee_id, Employee_n
5 min read
URL fields in serializers - Django REST Framework
In Django REST Framework the very concept of Serializing is to convert DB data to a datatype that can be used by javascript. Every serializer comes with some fields (entries) which are going to be processed. For example if you have a class with name Employee and its fields as Employee_id, Employee_n
5 min read
Get Request.User in Django-Rest-Framework Serializer
In Django Rest Framework (DRF), serializers are used to transform complex data types, such as queryset and model instances, into native Python data types. One common requirement is to access the request.user within a serializer, which is particularly useful when you need to customize the serializati
5 min read
JSONField in serializers - Django REST Framework
In Django REST Framework the very concept of Serializing is to convert DB data to a datatype that can be used by javascript. Every serializer comes with some fields (entries) which are going to be processed. For example if you have a class with name Employee and its fields as Employee_id, Employee_n
4 min read
Date and time fields in serializers - Django REST Framework
In Django REST Framework the very concept of Serializing is to convert DB data to a datatype that can be used by javascript. Every serializer comes with some fields (entries) which are going to be processed. For example if you have a class with name Employee and its fields as Employee_id, Employee_n
7 min read
Serializer Fields - Django REST Framework
Serializer comes with some fields (entries) that process data in and out of the serializer in Django REST Framework. The very motive of Serializing is to convert DB data to a datatype that can be used by javascript. For example, if you have a class with name Employee and its fields as Employee_id, E
13 min read
File upload Fields in Serializers - Django REST Framework
In Django REST Framework the very concept of Serializing is to convert DB data to a datatype that can be used by javascript. Every serializer comes with some fields (entries) which are going to be processed. For example if you have a class with name Employee and its fields as Employee_id, Employee_n
5 min read