Domanda

I am using django-rest-framework with django-filter. I need to retrieve a list of Widgets where the date property is None, but no matter what query I have tried, I am getting either empty responses or full, unfiltered responses.

Here's how I have defined the viewset and filterset.

class WidgetFilter(django_filters.FilterSet):
    date = django_filters.DateTimeFilter(name='date', lookup_type='exact')
    no_date = django_filters.DateTimefilter(name='date', lookup_type='isnull')
    class Meta:
        model = Widget
        fields = ['date',]

class WidgetSet(viewsets.ModelViewSet):
    model = Widget
    filter_class = WidgetFilter

The following queries result in empty [] responses:

?date=None
?date=0
?date=NULL
?date=False
?date=2012-05-24T11:20:06Z  # a known and correct date

The following queries result in all objects being returned:

?date=
?no_date=True
?no_date=False
?no_date=1
?no_date=0

Any help is very much appreciated! I have been unable to find any information on using dates (or passing None as a filter value) with django-filter specifically via django-rest-framework.


In case there is no more elegant way, here is how I bodged my workaround, but I'd still like to know of a solution that uses django-filter, if one exists.

class WidgetSet(viewsets.ModelViewSet):
    model = Widget

    def get_queryset(self):
        if 'no_date' in self.request.QUERY_PARAMS:
            return self.model.objects.filter(date=None)
        return self.model.objects.all()
È stato utile?

Soluzione

Specifying isnull directly in the filter's name argument as 'date__isnull' seems to be working for me with Django REST Framework 3.1.3.

class WidgetFilter(django_filters.FilterSet):
    date = django_filters.DateTimeFilter(name='date')
    no_date = django_filters.BooleanFilter(name='date__isnull')
    class Meta:
        model = Widget
        fields = []

Altri suggerimenti

I ran into a similar situation today, and it appears as if out of the box django rest framework* supports this filter as:

~/your_endpoint/?date__isnull=True

This matches up with how the the equivalent ORM query would look. If that's ugly, you can use the docs example to transform that query parameter to something else without having to override get_queryset

  • I'm using 2.4.3, but I don't believe this is a new thing

Running django-filter==1.0.4 and djangorestframework==3.6.3.

The correct answer didn't work because module paths have changed in django-filter (BooleanFilter moved to django_filters.rest_framework).

And the __isnull didn't work either, I had to use lookup_expr:

from django_filters import rest_framework as filters


class WidgetFilter(filters.FilterSet):
    no_date = filters.BooleanFilter(name='date', lookup_expr='isnull')

    class Meta:

        model = Widget
        fields = ( 'date')

Got the answer from https://github.com/carltongibson/django-filter/issues/743

Now I can filter by "no date" using http://localhost:8000/widgets/?no_date=True

In case there is no more elegant way, here is how I bodged my workaround, but I'd still like to know of a solution that uses django-filter, if one exists.

class WidgetSet(viewsets.ModelViewSet):
    model = Widget

    def get_queryset(self):
        if 'no_date' in self.request.QUERY_PARAMS:
            return self.model.objects.filter(date=None)
        return self.model.objects.all()

I've done this for choice filters, which is (arguably) more useful:

class NullableChoiceFilter(django_filters.ChoiceFilter):
    def __init__(self, **kwargs):
        choices = dict(kwargs['choices'])
        null_text = choices.pop(None, 'null')
        kwargs['choices'] = ((None, '------'), ('null', null_text)) + tuple(choices.items())
        super().__init__(**kwargs)

    def filter(self, qs, value):
        if value == 'null':
            return super().filter(qs, Lookup(lookup_type='isnull', value=True))
        else:
            return super().filter(qs, value)

and then

class MyFilterSet(filters.FilterSet):
    class Meta:
        model = ...

    @classmethod
    def filter_for_lookup(cls, f, lookup_type):
        if lookup_type == 'exact' and f.choices:
            return NullableChoiceFilter, {'choices': f.choices}
        return filters.FilterSet.filter_for_lookup(f, lookup_type)

I know this doesn't answer exactly your question, but this is the first google result for "django rest framework filter null."

Update: name must now be field_name... and filter_class is now filterset_class.

from django_filters import rest_framework as filters


class WidgetFilter(filters.FilterSet):
    no_date = filters.BooleanFilter(name='date', lookup_expr='isnull')

    class Meta:
        model = Widget
        fields = ('date')

class WidgetSet(viewsets.ModelViewSet):
    model = Widget
    filterset_class = WidgetFilter
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top