Search code examples
djangodjango-rest-frameworkdjango-filter

Filter for Multiple values of a field in DRF


In Amazon you can filter by Multiple Brands, eg. You select Nike, Puma, Adidas. The queryset that is sent back has products which belong to either of those brands.

if you have a model like:

class Product(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField()
    brand = models.CharField(max_length=100)

a serializer like:

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Product
        fields = "__all__"

viewset like:

class ProductViewSet(viewsets.ModelViewSet):
    serializer_class = serializers.ProductSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [filters.ProductMultipleFilterBackend ]

filters.py file:

from rest_framework import filters as drf_filters
from django_filters import rest_framework as df_filters

class MultipleField(MultipleChoiceField):
    def valid_value(self, value):   
        return True

class MultipleFilter(df_filters.MultipleChoiceFilter):
    field_class = MultipleField

class ProductMultipleFilterBackend(df_filters.FilterSet):
    brand = MultipleFilter(lookup_expr="icontains", field_name = "brand")
    search = MultipleFilter(lookup_expr="icontains", field_name = "description")

    class Meta:
        model = models.Product
        fields = ["brand", "search"]

the above code was something i found here. But it's like 5 years old and there might be a better way that i surely don't know about and i'm getting the following error on the code:

filter_queryset() takes 2 positional arguments but 4 were given

What would be the url structure for these requests: /someurl?brand=Nike,Adidas,Puma or /someurl?brand=[Nike,Adidas,Puma]

Thanks in Advance !!!!


Solution

  • The normal behavior for multiple choice is like ?f=v1&f=v2, while you could pass proper widget to change the default url structure to comma separated value by using CSVWidget

    from django_filters.fields import CSVWidget
    
    class ProductMultipleFilterBackend(df_filters.FilterSet):
        brand = MultipleFilter(
                     lookup_expr="icontains", 
                     field_name = "brand",
                     widget=CSVWidget
                )
         ...
    

    Then, you could filter using csv format like: url?brand=a,b