Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use filterset class in a nested route in Django REST Framework

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:

  • /users/[id]/
  • /posts/[id]/
  • /users/[id]/posts/

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?

like image 230
cezar Avatar asked Nov 27 '25 04:11

cezar


1 Answers

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.

like image 91
cezar Avatar answered Nov 29 '25 16:11

cezar