'How to add multiple filters when using Django Rest framework search_filter

I'm currently trying to create a fullstack website using django for the backend. One of the features I'm building is a search function. To do that, I used the built in django-rest SearchFilter, as well as the built in OrderingFilter so users can toggle how results are ordered. However, I also want users to be able to filter their results further, such as selecting a specific year, or a specific name, from a drowdown menu. So essentially, I want to be able to use a search filter and still specify another field that has to match exactly. For example, lets say the users searched for "Cat" and the results were as follows:


Name: Cat1
Description: A happy cat
Year: 2017


Name: Cat2
Description: A sad cat
Year: 2017


Name: Lion
Description: A large cat
Year: 2018


Name: Lion
Description: A large cat
Year: 2019

All of these results show up because they contain "cat" in either the name or description fields. But then, I want the user to be able to specify a year via a dropdown menu. So the menu would have 2017, 2018 and 2019 as options, and after selecting 2017, the results would be like this:


Name: Cat1
Description: A happy cat
Year: 2017


Name: Cat2
Description: A sad cat
Year: 2017

With the other years filtered out. I can't do this on the front end, because the database I'm working with has thousands of entries, meaning the results are returned in pages. This seems like a fairly straightforward problem, but none of the things I've tried have worked. Does anyone know the best way to approach this?

Here's the database model for the objects that are being searched:

class Measure(OverrideModel):

year = models.ForeignKey(Year, on_delete=models.PROTECT)
name = models.CharField(max_length=200)
description = models.TextField()

class Meta:
    constraints = [
        models.UniqueConstraint(
            fields=["duration", "code"],
            name=prefix("measure", "duration_code_uniq"),
        )
    ]

And here is the current view I'm using:

class MeasureSearchViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = models.Measure.objects.all()
    serializer_class = serializers.MeasureSerializer
    filter_backends = [OrderingFilter, SearchFilter]
    search_fields = ["name", "description"]
    ordering_fields = ["duration", "name"]

    def get_serializer(self, *args, **kwargs):
        return super().get_serializer(
            *args, program=self.program, tag=False, **kwargs
        )

Any advice would be appreciated, thanks :)



Solution 1:[1]

You need to combine search and filter mechanisms. Apart from defining search_fieldsYou need to add filter_fields attribute to your ViewSet and add DjangoFilterBackend to filter_backends list:

class MeasureSearchViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = models.Measure.objects.all()
    serializer_class = serializers.MeasureSerializer
    filter_backends = [OrderingFilter, SearchFilter, DjangoFilterBackend]
    search_fields = ["name", "description"]
    ordering_fields = ["duration", "name"]
    filterset_fields = ['year'] # enable filtering by year

    def get_serializer(self, *args, **kwargs):
        return super().get_serializer(
            *args, program=self.program, tag=False, **kwargs
        )

You can get more info at: https://www.django-rest-framework.org/api-guide/filtering/#generic-filtering

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1