Skip to content

Fix FilterSet proxy #4620

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 26 additions & 9 deletions rest_framework/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,32 @@ def get_schema_fields(self, view):
return []


class FilterSet(object):
def __new__(cls, *args, **kwargs):
warnings.warn(
"The built in 'rest_framework.filters.FilterSet' is pending deprecation. "
"You should use 'django_filters.rest_framework.FilterSet' instead.",
PendingDeprecationWarning
)
from django_filters.rest_framework import FilterSet
return FilterSet(*args, **kwargs)
if django_filters:
from django_filters.filterset import FilterSetMetaclass as DFFilterSetMetaclass
from django_filters.rest_framework.filterset import FilterSet as DFFilterSet

class FilterSetMetaclass(DFFilterSetMetaclass):
def __new__(cls, name, bases, attrs):
warnings.warn(
"The built in 'rest_framework.filters.FilterSet' is pending deprecation. "
"You should use 'django_filters.rest_framework.FilterSet' instead.",
PendingDeprecationWarning
)
return super(FilterSetMetaclass, cls).__new__(cls, name, bases, attrs)
_BaseFilterSet = DFFilterSet
else:
# Dummy metaclass just so we can give a user-friendly error message.
class FilterSetMetaclass(type):
def __init__(self, name, bases, attrs):
# Assert only on subclasses, so we can define FilterSet below.
if bases != (object,):
assert False, 'django-filter must be installed to use the `FilterSet` class'
super(FilterSetMetaclass, self).__init__(name, bases, attrs)
_BaseFilterSet = object
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if it'd be marginally cleaner to have FilterSet declaration inside the if case, rather than after it.

Then the else case could just have a FilterSet stub that raises an assertion error, rather than having a FilterSet metaclass.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The metaclass is required if we want the assert to fire when FilterSet is subclassed. That way the context of the error is at the actual point of use. Another alternative is a dummy class with an assert in __init__, but then the error won't happen until you hit the view.



class FilterSet(six.with_metaclass(FilterSetMetaclass, _BaseFilterSet)):
pass


class DjangoFilterBackend(BaseFilterBackend):
Expand Down
23 changes: 23 additions & 0 deletions tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,23 @@ class Meta:
model = BaseFilterableItem
fields = '__all__'

# Test the same filter using the deprecated internal FilterSet class.
class BaseFilterableItemFilterWithProxy(filters.FilterSet):
text = django_filters.CharFilter()

class Meta:
model = BaseFilterableItem
fields = '__all__'

class BaseFilterableItemFilterRootView(generics.ListCreateAPIView):
queryset = FilterableItem.objects.all()
serializer_class = FilterableItemSerializer
filter_class = BaseFilterableItemFilter
filter_backends = (filters.DjangoFilterBackend,)

class BaseFilterableItemFilterWithProxyRootView(BaseFilterableItemFilterRootView):
filter_class = BaseFilterableItemFilterWithProxy

# Regression test for #814
class FilterFieldsQuerysetView(generics.ListCreateAPIView):
queryset = FilterableItem.objects.all()
Expand Down Expand Up @@ -296,6 +307,18 @@ def test_base_model_filter(self):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)

@unittest.skipUnless(django_filters, 'django-filter not installed')
def test_base_model_filter_with_proxy(self):
"""
The `get_filter_class` model checks should allow base model filters.
"""
view = BaseFilterableItemFilterWithProxyRootView.as_view()

request = factory.get('/?text=aaa')
response = view(request).render()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 1)

@unittest.skipUnless(django_filters, 'django-filter not installed')
def test_unknown_filter(self):
"""
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy