Skip to content

Commit 3c7ed1f

Browse files
author
Ryan P Kilby
committed
Fix #84, temp fix for __all__
1 parent ee3cb8c commit 3c7ed1f

File tree

4 files changed

+69
-7
lines changed

4 files changed

+69
-7
lines changed

rest_framework_filters/filterset.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,14 @@ def convert__all__(attrs):
9999
"""
100100
Extract Meta.fields and convert any fields w/ `__all__`
101101
to a declared AllLookupsFilter.
102+
103+
This is a temporary hack to fix #87.
102104
"""
103105
meta = attrs.get('Meta', None)
106+
model = getattr(meta, 'model', None)
104107
fields = getattr(meta, 'fields', None)
105108

106-
if isinstance(fields, dict):
109+
if model and isinstance(fields, dict):
107110
for name, lookups in six.iteritems(fields.copy()):
108111
if lookups == filters.ALL_LOOKUPS:
109112
warnings.warn(
@@ -114,8 +117,10 @@ def convert__all__(attrs):
114117
lookups = '__all__'
115118

116119
if lookups == '__all__':
117-
del fields[name]
118-
attrs[name] = filters.AllLookupsFilter()
120+
# Modifying fields is incorrect. The correct behavior will
121+
# require hooking into filters_for_model
122+
field = model._meta.get_field(name)
123+
fields[name] = utils.lookups_for_field(field)
119124

120125

121126
class FilterSet(six.with_metaclass(FilterSetMetaclass, filterset.FilterSet)):

rest_framework_filters/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import django
55
from django.db.models.constants import LOOKUP_SEP
66
from django.db.models.expressions import Expression
7+
from django.db.models.fields.related import ForeignObject
78
from django.db.models.lookups import Transform
89
from django.utils import six
910

@@ -12,6 +13,11 @@ def lookups_for_field(model_field):
1213
"""
1314
Generates a list of all possible lookup expressions for a model field.
1415
"""
16+
# This is a hack to work around:
17+
# https://github.com/django/django/pull/6906
18+
if isinstance(model_field, ForeignObject):
19+
return ['exact', 'gt', 'gte', 'lt', 'lte', 'in', 'isnull']
20+
1521
lookups = []
1622

1723
for expr, lookup in six.iteritems(class_lookups(model_field)):

tests/test_filterset.py

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from django.test import TestCase
88

99
from rest_framework_filters import FilterSet, filters
10+
from django_filters.filters import BaseInFilter
1011

1112
from .testapp.models import (
1213
User, Note, Post, Cover, Page, A, B, C, Person, Tag, BlogPost,
@@ -156,7 +157,48 @@ def test_relatedfilter(self):
156157
note = list(f)[0]
157158
self.assertEqual(note.title, "Hello Test 4")
158159

159-
def test_relatedfilter_combined_with_alllookups(self):
160+
def test_alllookupsfilter_for_relation(self):
161+
# See: https://github.com/philipn/django-rest-framework-filters/issues/84
162+
class F(FilterSet):
163+
class Meta:
164+
model = Note
165+
fields = {
166+
'author': '__all__',
167+
}
168+
169+
self.assertIsInstance(F.base_filters['author'], filters.ModelChoiceFilter)
170+
self.assertIsInstance(F.base_filters['author__in'], BaseInFilter)
171+
172+
def test_relatedfilter_combined_with__all__(self):
173+
# ensure that related filter is compatible with __all__ lookups.
174+
class F(FilterSet):
175+
author = filters.RelatedFilter(UserFilter)
176+
177+
class Meta:
178+
model = Note
179+
fields = {
180+
'author': '__all__',
181+
}
182+
183+
self.assertIsInstance(F.base_filters['author'], filters.RelatedFilter)
184+
self.assertIsInstance(F.base_filters['author__in'], BaseInFilter)
185+
186+
def test_filter_persistence_with__all__(self):
187+
# ensure that __all__ does not overwrite declared filters.
188+
class F(FilterSet):
189+
name = filters.ChoiceFilter(lookup_expr='iexact')
190+
191+
class Meta:
192+
model = Person
193+
fields = {
194+
'name': '__all__',
195+
}
196+
197+
self.assertIsInstance(F.base_filters['name'], filters.ChoiceFilter)
198+
199+
def test_relatedfilter_for_related_alllookups(self):
200+
# ensure that filters work for AllLookupsFilter across a RelatedFilter.
201+
160202
# Test that the default exact filter works
161203
GET = {
162204
'author': User.objects.get(username='user2').pk,
@@ -195,7 +237,7 @@ def test_relatedfilter_combined_with_alllookups(self):
195237
f = NoteFilterWithRelatedAll(GET, queryset=Note.objects.all())
196238
self.assertEqual(len(list(f)), 4)
197239

198-
def test_relatedfilter_combined_with_alllookups_and_different_filter_name(self):
240+
def test_relatedfilter_for_related_alllookups_and_different_filter_name(self):
199241
# Test that the default exact filter works
200242
GET = {
201243
'writer': User.objects.get(username='user2').pk,
@@ -294,7 +336,7 @@ def test_m2m_relation(self):
294336
}
295337
f = BlogPostFilter(GET, queryset=BlogPost.objects.all())
296338
self.assertEqual(len(list(f)), 2)
297-
titles = set([p.title for p in f])
339+
titles = set([person.title for person in f])
298340
self.assertEqual(titles, set(["First post", "Second post"]))
299341

300342
def test_nonexistent_related_field(self):

tests/test_utils.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from rest_framework_filters import utils
77

8-
from .testapp.models import Person
8+
from .testapp.models import Person, Note
99

1010

1111
class LookupsForFieldTests(TestCase):
@@ -29,6 +29,15 @@ def test_transformed_field(self):
2929
self.assertIn('year__exact', lookups)
3030
self.assertIn('date__year__exact', lookups)
3131

32+
def test_relation_field(self):
33+
# ForeignObject relations are special cased currently
34+
model_field = Note._meta.get_field('author')
35+
lookups = utils.lookups_for_field(model_field)
36+
37+
self.assertIn('exact', lookups)
38+
self.assertIn('in', lookups)
39+
self.assertNotIn('regex', lookups)
40+
3241

3342
@unittest.skipIf(django.VERSION < (1, 9), "version does not support transformed lookup expressions")
3443
class LookupsForTransformTests(TestCase):

0 commit comments

Comments
 (0)