philipn/django-rest-framework-filters

KeyError when using django-filter>=2.0

florimondmanca opened this issue · 2 comments

Steps to Reproduce

Note: the model I was using was replaced with a generic MyModel (for confidentiality concerns), so I have not run this code myself but hopefully it should be enough to reproduce the issue.

# models.py

class MyModel(models.Model):
    name = models.TextField()


# serializers.py

from rest_framework import serializers

from .models import MyModel

class MyModelSerializer(serializers.ModelSerializer):

    class Meta:
        model = MyModel
        fields = ('name',)


# filters.py

class MyModelFilter(filters.FilterSet):

    class Meta:
        model = MyModel
        fields = {
            'name': ['exact'],
        }


# views.py

from rest_framework import mixins
from rest_framework_filters.backends import DjangoFilterBackend

from .models import MyModel
from .serializers import MyModelSerializer
from .filters import MyModelFilter

class MyModelViewSet(mixins.ModelViewSet):

    queryset = MyModel.objects.all()
    serializer_class = MyModelSerializer

    filter_backends = (DjangoFilterBackend,)
    search_fields = ('name',)
    filter_class = MyModelFilter


# urls.py

import myapp.views

...
router.register('foo', myapp.views.MyModelViewSet)
...

Then, in a test, performing:

response = self.client.get('/foo/', data={'search': 'name'})

raises the following error:

Traceback (most recent call last):
  File "/app/tests/test_api.py", line 156, in test_foo
    response = self.client.get('/foo/', data={'search': 'name'})
  File "/usr/local/lib/python3.6/site-packages/rest_framework/test.py", line 292, in get
    response = super(APIClient, self).get(path, data=data, **extra)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/test.py", line 209, in get
    return self.generic('GET', path, **r)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/test.py", line 238, in generic
    method, path, data, content_type, secure, **extra)
  File "/usr/local/lib/python3.6/site-packages/django/test/client.py", line 416, in generic
    return self.request(**r)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/test.py", line 289, in request
    return super(APIClient, self).request(**kwargs)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/test.py", line 241, in request
    request = super(APIRequestFactory, self).request(**kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/test/client.py", line 501, in request
    six.reraise(*exc_info)
  File "/usr/local/lib/python3.6/site-packages/django/utils/six.py", line 686, in reraise
    raise value
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
    response = get_response(request)
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/viewsets.py", line 103, in view
    return self.dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 483, in dispatch
    response = self.handle_exception(exc)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 443, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 480, in dispatch
    response = handler(request, *args, **kwargs)
  File "/usr/local/lib/python3.6/site-packages/rest_framework/mixins.py", line 40, in list
    queryset = self.filter_queryset(self.get_queryset())
  File "/usr/local/lib/python3.6/site-packages/rest_framework/generics.py", line 152, in filter_queryset
    queryset = backend().filter_queryset(self.request, queryset, self)
  File "/usr/local/lib/python3.6/site-packages/rest_framework_filters/backends.py", line 34, in filter_queryset
    return super(DjangoFilterBackend, self).filter_queryset(request, queryset, view)
  File "/usr/local/lib/python3.6/site-packages/django_filters/rest_framework/backends.py", line 96, in filter_queryset
    return filterset.qs
  File "/usr/local/lib/python3.6/site-packages/rest_framework_filters/filterset.py", line 266, in qs
    qs = super(FilterSet, self).qs
  File "/usr/local/lib/python3.6/site-packages/django_filters/filterset.py", line 237, in qs
    qs = self.filter_queryset(qs)
  File "/usr/local/lib/python3.6/site-packages/django_filters/filterset.py", line 224, in filter_queryset
    queryset = self.filters[name].filter(queryset, value)
KeyError: 'name'

Previously, the KeyError was not raised and the filtering worked perfectly fine.

Environment

Docker image: python:3.6

Possible Solution

I am using django-rest-framework-filters==0.10.2.

The setup.py file in the repo states that it requires django_filter>=1.0.

install_requires=[
    'djangorestframework',
    'django-filter>=1.0.0',
],

So, as django-filter==2.0 was released recently, when installing requirements from scratch again today, it installed that new version and the bug started to occur.

I was able to make it work again my explicitly stating django-filter<2.0, which installs django-filter==1.1. So it should be possible to quick-fix this by updating install_requires in setup.py, waiting for a release that supports django-filter>=2.0.

Hi @florimondmanca. Yes, the issue is that the current release (0.10.2) is not compatible with django-filter 2.0.0. I'm finishing up a few related items for the 1.0 milestone (mostly updating the docs), and then the latest release should be compatible. I'll add a temporary note to the README so users are aware of the project status.

Hi @florimondmanca, I went ahead and created a 0.10.x branch that sets the django-filter version to 1.x-compatible releases. @philipn should be able to create a post-release at some point.