Skip to content

Commit 7119eaa

Browse files
author
Ryan P Kilby
authored
Merge pull request #144 from rpkilby/update-django-filter
Update django-filter to 1.0
2 parents 27a8147 + b27e92b commit 7119eaa

File tree

6 files changed

+67
-58
lines changed

6 files changed

+67
-58
lines changed

rest_framework_filters/filters.py

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,30 @@
44
from django.utils import six
55

66
from django_filters.rest_framework.filters import *
7+
from rest_framework_filters.utils import import_class
78

89

910
ALL_LOOKUPS = '__all__'
1011

1112

12-
def _import_class(path):
13-
module_path, class_name = path.rsplit('.', 1)
14-
class_name = str(class_name) # Ensure not unicode on py2.x
15-
module = __import__(module_path, fromlist=[class_name], level=0)
16-
return getattr(module, class_name)
13+
class AutoFilter(Filter):
14+
def __init__(self, *args, **kwargs):
15+
self.lookups = kwargs.pop('lookups', [])
1716

17+
super(AutoFilter, self).__init__(*args, **kwargs)
1818

19-
class RelatedFilter(ModelChoiceFilter):
20-
def __init__(self, filterset, lookups=None, *args, **kwargs):
19+
20+
class RelatedFilter(AutoFilter, ModelChoiceFilter):
21+
def __init__(self, filterset, *args, **kwargs):
2122
self.filterset = filterset
22-
self.lookups = lookups
23-
return super(RelatedFilter, self).__init__(*args, **kwargs)
23+
kwargs.setdefault('lookups', None)
24+
25+
super(RelatedFilter, self).__init__(*args, **kwargs)
2426

2527
def filterset():
2628
def fget(self):
2729
if isinstance(self._filterset, six.string_types):
28-
self._filterset = _import_class(self._filterset)
30+
self._filterset = import_class(self._filterset)
2931
return self._filterset
3032

3133
def fset(self, value):
@@ -34,12 +36,15 @@ def fset(self, value):
3436
return locals()
3537
filterset = property(**filterset())
3638

37-
@property
38-
def field(self):
39-
# if no queryset is provided, default to the filterset's default queryset
40-
self.extra.setdefault('queryset', self.filterset._meta.model._default_manager.all())
41-
return super(RelatedFilter, self).field
39+
def get_queryset(self, request):
40+
queryset = super(RelatedFilter, self).get_queryset(request)
41+
if queryset is not None:
42+
return queryset
43+
return self.filterset._meta.model._default_manager.all()
44+
4245

46+
class AllLookupsFilter(AutoFilter):
47+
def __init__(self, *args, **kwargs):
48+
kwargs.setdefault('lookups', ALL_LOOKUPS)
4349

44-
class AllLookupsFilter(Filter):
45-
lookups = ALL_LOOKUPS
50+
super(AllLookupsFilter, self).__init__(*args, **kwargs)

rest_framework_filters/filterset.py

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,38 +17,45 @@
1717
class FilterSetMetaclass(filterset.FilterSetMetaclass):
1818
def __new__(cls, name, bases, attrs):
1919
new_class = super(FilterSetMetaclass, cls).__new__(cls, name, bases, attrs)
20+
2021
opts = copy.deepcopy(new_class._meta)
22+
orig_meta = new_class._meta
23+
24+
declared_filters = new_class.declared_filters.copy()
25+
orig_declared = new_class.declared_filters
2126

22-
# If no model is defined, skip all lookups processing
27+
# If no model is defined, skip auto filter processing
2328
if not opts.model:
2429
return new_class
2530

26-
# Determine declared filters and filters to generate lookups from. Declared
27-
# filters have precedence over generated filters and should not be overwritten.
28-
declared_filters, lookups_filters = OrderedDict(), OrderedDict()
29-
for name, f in six.iteritems(new_class.declared_filters):
30-
if isinstance(f, (filters.AllLookupsFilter, filters.RelatedFilter)):
31-
lookups_filters[name] = f
31+
# Generate filters for auto filters
32+
auto_filters = OrderedDict([
33+
(param, f) for param, f in six.iteritems(new_class.declared_filters)
34+
if isinstance(f, filters.AutoFilter)
35+
])
3236

33-
# `AllLookupsFilter` is an exception, as it should be overwritten
34-
if not isinstance(f, filters.AllLookupsFilter):
35-
declared_filters[name] = f
37+
# Remove auto filters from declared_filters so that they *are* overwritten
38+
# RelatedFilter is an exception, and should *not* be overwritten
39+
for param, f in six.iteritems(auto_filters):
40+
if not isinstance(f, filters.RelatedFilter):
41+
del declared_filters[param]
3642

37-
# generate filters for AllLookups/Related filters
38-
# name is the parameter name on the filterset, f.name is the model field's name
39-
for name, f in six.iteritems(lookups_filters):
43+
for param, f in six.iteritems(auto_filters):
4044
opts.fields = {f.name: f.lookups or []}
41-
new_filters = new_class.filters_for_model(opts.model, opts)
4245

43-
# filters_for_model generate param names from the model field name
44-
# replace model field name with the parameter name from the filerset
46+
# patch, generate auto filters
47+
new_class._meta, new_class.declared_filters = opts, declared_filters
48+
generated_filters = new_class.get_filters()
49+
50+
# get_filters() generates param names from the model field name
51+
# Replace the field name with the parameter name from the filerset
4552
new_class.base_filters.update(OrderedDict(
46-
(param.replace(f.name, name, 1), v)
47-
for param, v in six.iteritems(new_filters)
53+
(gen_param.replace(f.name, param, 1), gen_f)
54+
for gen_param, gen_f in six.iteritems(generated_filters)
4855
))
4956

50-
# re-apply declared filters (sans `AllLookupsFilter`s)
51-
new_class.base_filters.update(declared_filters)
57+
new_class._meta, new_class.declared_filters = orig_meta, orig_declared
58+
5259
return new_class
5360

5461
@property
@@ -68,26 +75,17 @@ class FilterSet(six.with_metaclass(FilterSetMetaclass, rest_framework.FilterSet)
6875
_subset_cache = {}
6976

7077
@classmethod
71-
def filters_for_model(cls, model, opts):
72-
fields = opts.fields
73-
74-
if not isinstance(fields, dict):
75-
return super(FilterSet, cls).filters_for_model(model, opts)
78+
def get_fields(cls):
79+
fields = super(FilterSet, cls).get_fields()
7680

77-
# replace all '__all__' values by the resolved list of all lookups
78-
fields = fields.copy()
7981
for name, lookups in six.iteritems(fields):
8082
if lookups == filters.ALL_LOOKUPS:
81-
field = get_model_field(model, name)
83+
field = get_model_field(cls._meta.model, name)
8284
fields[name] = utils.lookups_for_field(field)
8385

84-
return filterset.filters_for_model(
85-
model, fields, opts.exclude,
86-
cls.filter_for_field,
87-
cls.filter_for_reverse_field
88-
)
86+
return fields
8987

90-
def get_filters(self):
88+
def expand_filters(self):
9189
"""
9290
Build a set of filters based on the requested data. The resulting set
9391
will walk `RelatedFilter`s to recursively build the set of filters.
@@ -128,7 +126,7 @@ def get_filters(self):
128126
filterset = subset_class(data=subset_data)
129127

130128
# modify filter names to account for relationship
131-
for related_name, related_f in six.iteritems(filterset.get_filters()):
129+
for related_name, related_f in six.iteritems(filterset.expand_filters()):
132130
related_name = LOOKUP_SEP.join([filter_name, related_name])
133131
related_f.name = LOOKUP_SEP.join([f.name, related_f.name])
134132
requested_filters[related_name] = related_f
@@ -258,7 +256,7 @@ def cache_set(cls, key, value):
258256
@property
259257
def qs(self):
260258
available_filters = self.filters
261-
requested_filters = self.get_filters()
259+
requested_filters = self.expand_filters()
262260

263261
self.filters = requested_filters
264262
qs = super(FilterSet, self).qs

rest_framework_filters/utils.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@
99
from django.utils import six
1010

1111

12+
def import_class(path):
13+
module_path, class_name = path.rsplit('.', 1)
14+
class_name = str(class_name) # Ensure not unicode on py2.x
15+
module = __import__(module_path, fromlist=[class_name], level=0)
16+
return getattr(module, class_name)
17+
18+
1219
def lookups_for_field(model_field):
1320
"""
1421
Generates a list of all possible lookup expressions for a model field.

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def get_package_data(package):
4343
zip_safe=False,
4444
install_requires=[
4545
'djangorestframework',
46-
'django-filter>=0.15.0',
46+
'django-filter>=1.0.0',
4747
],
4848
classifiers=[
4949
'Development Status :: 5 - Production/Stable',

tests/test_backends.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ class SimpleViewSet(views.FilterFieldsUserViewSet):
5454
<p>
5555
<label for="id_username">Username:</label>
5656
<input id="id_username" name="username" type="text" />
57-
<span class="helptext">Filter</span>
5857
</p>
5958
<button type="submit" class="btn btn-primary">Submit</button>
6059
</form>

tests/test_filterset.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ def test_exclude_property(self):
343343
}
344344

345345
filterset = TagFilter(GET, queryset=Tag.objects.all())
346-
requested_filters = filterset.get_filters()
346+
requested_filters = filterset.expand_filters()
347347

348348
self.assertTrue(requested_filters['name__contains!'].exclude)
349349

@@ -357,7 +357,7 @@ def test_filter_and_exclude(self):
357357
}
358358

359359
filterset = TagFilter(GET, queryset=Tag.objects.all())
360-
requested_filters = filterset.get_filters()
360+
requested_filters = filterset.expand_filters()
361361

362362
self.assertFalse(requested_filters['name__contains'].exclude)
363363
self.assertTrue(requested_filters['name__contains!'].exclude)
@@ -368,7 +368,7 @@ def test_related_exclude(self):
368368
}
369369

370370
filterset = BlogPostFilter(GET, queryset=BlogPost.objects.all())
371-
requested_filters = filterset.get_filters()
371+
requested_filters = filterset.expand_filters()
372372

373373
self.assertTrue(requested_filters['tags__name__contains!'].exclude)
374374

0 commit comments

Comments
 (0)