Search code examples
django-rest-frameworkdjango-filter

What is the "'string' in list" equivalent in django_filter?


There are entries added to a Django model and they look like:

"makes": [\"Ford\", \"Opel\", \"Mazda\", \"Toyota\", \"Volkswagen\"]

I was trying to filter out all entries containing a make, a list of makes or a partial name of a make, implementing custom filter:

from django_filters import BaseInFilter, CharFilter, FilterSet
from django_filters.rest_framework import DjangoFilterBackend
[...]


class ValueInFilter(BaseInFilter, CharFilter):
    pass


class Value(FilterSet):
    value__in = ValueInFilter(field_name='makes', lookup_expr='in')

    class Meta:
        model = Dealer
        fields = '__all__'


class DealerViewSet(viewsets.ModelViewSet):
    queryset = Dealer.objects.all()
    serializer_class = DealerSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['type', 'city', 'makes', 'reputation']

Without custom filter, I'm only able to query something like /api/dealers/?makes=["Ford"] to get results containing only Ford dealers. (1) How should I configure filter to strip brackets and quotation marks from the URL?

(2) I'd like to get also dealers selling Fords and other makes, like in:

makes = ["Ford", "Opel", "Mazda", "Toyota", "Volkswagen"]
"Ford" in makes
True

(3) Also, I'd love to be able to just type /api/dealers/?makes=f and get Ford dealers, and Fiat dealers.

How to make it possible?


Solution

  • Using model designer's privilege, I rebuilt a bit the way the makes were stored.

    By applying ",".join(makes) I got something like: makes = "Ford,Opel,Mazda,Toyota,Volkswagen" (it's a single string; technically a CSV).

    And now I'm able to search instead of filtering. Viewsets require adding the following lines:

    from rest_framework import filters
    [...]
    
    filter_backends = [filters.SearchFilter]
    search_fields = ['$makes']
    

    The $ sign gives opportunity to perform RegEx searches, so querying /api/dealers/?search=f will return all makes containing letter F (case insensitive). Still, filtering is also possible: /api/dealers/?city=Paris&search=f would return all Fords, Fiats, etc. from Paris. In this specific case only searching for a make is possible. If one needs to extend searching to another fields, it has to be reflected in the search_fields values.