We have a REST API created with Django and Django REST Framework. With the package django-filter I've created a FilterSet class which I want to use in a nested route.
For illustration, we have model classes User, Post, Tag. A Post has one author (User) and can have many (or none) Tags.
The following endpoints are present:
The FilterSet class looks like this:
class PostFilterSet(filters.FilterSet):
tags = filters.ModelChoiceFilter(queryset=Tag.objects.all())
class Meta:
model = Post
fields = ("tags",)
We use it in the viewset for Posts:
class PostViewSet(viewsets.ModelViewSet):
serializer_class = PostSerializer
queryset = Post.objects.all()
filter_backends = [filters.DjangoFilterBackend]
filterset_class = PostFilterSet
Now this is working well and the list of posts can be filtered by tag, like this:
/posts/?tags=some_tag
In the UserViewSet we have a nested route created with the decorator action:
class UserViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
filter_backends = [filters.DjangoFilterBackend]
filterset_class = UserFilterSet
@action(methods=["get"], detail=True)
def posts(self, request, pk):
# logic to fetch posts for the given user
return Response(serializer.data)
We want to filter the list of posts for a given user (author) tagged by some tag:
/users/[id]/posts/?tags=some_tag
I want to use the PostFilterSet class in the nested route above. Is this possible? If yes, how should it be done?
This is how I have solved this problem:
class UserViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
filter_backends = [filters.DjangoFilterBackend]
filterset_class = UserFilterSet
@action(methods=["get"], detail=True)
def posts(self, request, pk):
user = get_object_or_404(User, pk=pk)
posts = Post.objects.filter(author=user)
filter_class = PostFilterSet(request.query_params, queryset=posts)
posts = filter_class.qs
serializer = PostSerializer(posts, many=True, context={"request": request})
return Response(serializer.data)
The bold lines are showing how to call the filterset class manually and filter the queryset.
Now if the client calls the endpoint like this:
/users/[id]/posts/?tags=some_tag
then the query parameters (in this case it's only one - tags) will be used to filter the queryset.
This answer gave me an idea:
https://stackoverflow.com/a/27161942/3848833
If you have implemented pagination then this isn't going to work straightforward and you'll have to adjust the code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With