Skip to content

Commit 98df932

Browse files
spearkitomchristie
authored andcommitted
Fix FilterSet proxy (encode#4620)
1 parent d92b24a commit 98df932

File tree

2 files changed

+49
-9
lines changed

2 files changed

+49
-9
lines changed

rest_framework/filters.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,32 @@ def get_schema_fields(self, view):
3737
return []
3838

3939

40-
class FilterSet(object):
41-
def __new__(cls, *args, **kwargs):
42-
warnings.warn(
43-
"The built in 'rest_framework.filters.FilterSet' is pending deprecation. "
44-
"You should use 'django_filters.rest_framework.FilterSet' instead.",
45-
PendingDeprecationWarning
46-
)
47-
from django_filters.rest_framework import FilterSet
48-
return FilterSet(*args, **kwargs)
40+
if django_filters:
41+
from django_filters.filterset import FilterSetMetaclass as DFFilterSetMetaclass
42+
from django_filters.rest_framework.filterset import FilterSet as DFFilterSet
43+
44+
class FilterSetMetaclass(DFFilterSetMetaclass):
45+
def __new__(cls, name, bases, attrs):
46+
warnings.warn(
47+
"The built in 'rest_framework.filters.FilterSet' is pending deprecation. "
48+
"You should use 'django_filters.rest_framework.FilterSet' instead.",
49+
PendingDeprecationWarning
50+
)
51+
return super(FilterSetMetaclass, cls).__new__(cls, name, bases, attrs)
52+
_BaseFilterSet = DFFilterSet
53+
else:
54+
# Dummy metaclass just so we can give a user-friendly error message.
55+
class FilterSetMetaclass(type):
56+
def __init__(self, name, bases, attrs):
57+
# Assert only on subclasses, so we can define FilterSet below.
58+
if bases != (object,):
59+
assert False, 'django-filter must be installed to use the `FilterSet` class'
60+
super(FilterSetMetaclass, self).__init__(name, bases, attrs)
61+
_BaseFilterSet = object
62+
63+
64+
class FilterSet(six.with_metaclass(FilterSetMetaclass, _BaseFilterSet)):
65+
pass
4966

5067

5168
class DjangoFilterBackend(BaseFilterBackend):

tests/test_filters.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,23 @@ class Meta:
7979
model = BaseFilterableItem
8080
fields = '__all__'
8181

82+
# Test the same filter using the deprecated internal FilterSet class.
83+
class BaseFilterableItemFilterWithProxy(filters.FilterSet):
84+
text = django_filters.CharFilter()
85+
86+
class Meta:
87+
model = BaseFilterableItem
88+
fields = '__all__'
89+
8290
class BaseFilterableItemFilterRootView(generics.ListCreateAPIView):
8391
queryset = FilterableItem.objects.all()
8492
serializer_class = FilterableItemSerializer
8593
filter_class = BaseFilterableItemFilter
8694
filter_backends = (filters.DjangoFilterBackend,)
8795

96+
class BaseFilterableItemFilterWithProxyRootView(BaseFilterableItemFilterRootView):
97+
filter_class = BaseFilterableItemFilterWithProxy
98+
8899
# Regression test for #814
89100
class FilterFieldsQuerysetView(generics.ListCreateAPIView):
90101
queryset = FilterableItem.objects.all()
@@ -296,6 +307,18 @@ def test_base_model_filter(self):
296307
self.assertEqual(response.status_code, status.HTTP_200_OK)
297308
self.assertEqual(len(response.data), 1)
298309

310+
@unittest.skipUnless(django_filters, 'django-filter not installed')
311+
def test_base_model_filter_with_proxy(self):
312+
"""
313+
The `get_filter_class` model checks should allow base model filters.
314+
"""
315+
view = BaseFilterableItemFilterWithProxyRootView.as_view()
316+
317+
request = factory.get('/?text=aaa')
318+
response = view(request).render()
319+
self.assertEqual(response.status_code, status.HTTP_200_OK)
320+
self.assertEqual(len(response.data), 1)
321+
299322
@unittest.skipUnless(django_filters, 'django-filter not installed')
300323
def test_unknown_filter(self):
301324
"""

0 commit comments

Comments
 (0)