filterset lookups for annotated fields

1.6k views Asked by At

i have a problem to create a filterset for one of my ModelViewSets to provide filter functionality (incl. all allowed lookups e.g. in, gt, lt, ...) for annotated fields in my api. My Code looks as following:

import rest_framework_filters as filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.viewsets import ModelViewSet

class MyViewSet(ModelViewSet):

    class MyFilterSet(filters.FilterSet):
         """ internal FilterSet """
         min_value = filters.AllLookupsFilter()

         class Meta:
             model = MyModel
             fields = {
                 'id': '__all__',
                 'name': '__all__',
                 'type': '__all__',
                 'managed_by': '__all__',
                 'created_by': '__all__',
             }

    filter_backends = (DjangoFilterBackend,)
    filter_class = MyFilterSet

    def get_queryset(self):
        return super().get_queryset().annotate(min_value=Min('field')).order_by('id')

When i try to start my app the following exception occurs:

TypeError: 'Meta.fields' contains fields that are not defined on this FilterSet: min_value

Any idea whats wrong with my code?


I found out that my "djangorestframework-filters" module was outdated. I made the update to the recent version (0.10.2) and now the error changed to:

File "C:\Dev\Python\Python36\lib\site-packages\rest_framework_filters\filterset.py", line 44, in __new__ opts.fields = {f.name: f.lookups or []}
AttributeError: 'AllLookupsFilter' object has no attribute 'name'

I already updated all dependent modules. Any ideas whats the problem here?

1

There are 1 answers

0
Sherpa On

AllLookupsFilter/AutoFilter hook into the same filter generation code that is used in Meta.fields. Your filterset is effectively equivalent to:

class MyFilterSet(filters.FilterSet):
    class Meta:
        model = MyModel
        fields = {
            'min_value': '__all__',
            ...
        }

Filter generation uses a model's fields as the base for determining the appropriate type of a filter. The fundamental issue is that annotations don't exist on the model class as a field - they are added to a QuerySet instance and can't be inspected for type information.

Instead, it's necessary to declare filters for annotations.

class MyFilterSet(filters.FilterSet):
    min_value = filters.NumberFilter(field_name='min_value', lookup_expr='exact')
    min_value__gt = filters.NumberFilter(field_name='min_value', lookup_expr='gt')
    ...