How do you use the django-filter package with a list of parameters?

10.5k views Asked by At

I want to filter my model with django-filter. It works fine if I filter by one id like:

http://localhost:8000/accommodations?accommodationType_id=1

But I don't know how I can filter by multiple ids like.

http://localhost:8000/accommodations?accommodationType_id=1,2

My actual ViewSet looks like this:

class AccommodationViewSet(viewsets.ReadOnlyModelViewSet):
    """
        REST API endpoint for 'accommodation' resource
    """
    queryset = Accommodation.objects.all()
    serializer_class = AccommodationSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = ('accommodationType_id', 'name')

I hope there is a solution.

3

There are 3 answers

1
abuder On BEST ANSWER

I found the following solution for my problem :)

https://gist.github.com/aBuder/654fb945f085b17358d8

from webapp.serializers import *
from rest_framework import viewsets
from rest_framework import filters
from django_filters import Filter, FilterSet


class ListFilter(Filter):
    def filter(self, qs, value):
        if not value:
            return qs

        # For django-filter versions < 0.13, use lookup_type instead of lookup_expr
        self.lookup_expr = 'in'
        values = value.split(',')
        return super(ListFilter, self).filter(qs, values)


class AccommodationFilter(FilterSet):
    ids = ListFilter(name='id')
    accommodationType_ids = ListFilter(name='accommodationType_id')

    class Meta:
        model = Accommodation
        fields = ['ids', 'accommodationType_ids']


class AccommodationViewSet(viewsets.ReadOnlyModelViewSet):
    """
        REST API endpoint for 'accommodation' resource
    """
    queryset = Accommodation.objects.all()
    serializer_class = AccommodationSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = AccommodationFilter
2
Josh Correia On

Now there is an even simpler way to achieve this. Deep within the django-filter documentation, it mentions that you can use "a dictionary of field names mapped to a list of lookups".

Your code would be updated like so:

class AccommodationViewSet(viewsets.ReadOnlyModelViewSet):
    """
        REST API endpoint for 'accommodation' resource
    """
    queryset = Accommodation.objects.all()
    serializer_class = AccommodationSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_fields = {
        'accommodationType_id': ["in", "exact"], # note the 'in' field
        'name': ["exact"]
    }

Now in the URL you would add __in to the filter before supplying your list of parameters and it would work as you expect:

http://localhost:8000/accommodations?accommodationType_id__in=1,2

The django-filter documentation on what lookup filters are available is quite poor, but the in lookup filter is mentioned in the Django documentation itself.

2
Agey On

I know it is an old question, but might be worth it to give an updated answer.

Django-filter contributors have added a field called BaseInFilter which you can combine with other filters to validate the content.

See the docs: https://django-filter.readthedocs.io/en/latest/ref/filters.html#baseinfilter

For example, this would work in your case:

from django_filters import rest_framework as filters


class NumberInFilter(filters.BaseInFilter, filters.NumberFilter):
    pass


class AccommodationFilter(filters.FilterSet):
    accommodationType_id_in = NumberInFilter(field_name='accommodationType_id', lookup_expr='in')

    class Meta:
        model = Accommodation
        fields = ['accommodationType_id_in', ]

Then you would be able to filter by a list of ids: http://localhost:8000/accommodations?accommodationType_id_in=1,2