Skip to content

Commit 992ac80

Browse files
author
Ryan P Kilby
committed
Refactor AllLookupsFilter eval into metaclass
- Evaluation now only occurs once during class creation. This should fix a minor performance issue with repeated inits of the FilterSet. - Dropped LOOKUP_TYPES from the FilterSet.
1 parent 4fd4d35 commit 992ac80

File tree

1 file changed

+35
-21
lines changed

1 file changed

+35
-21
lines changed

rest_framework_filters/filterset.py

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import absolute_import
22
from __future__ import unicode_literals
33

4-
from copy import copy
54
from collections import OrderedDict
65

76
try:
@@ -19,12 +18,42 @@
1918

2019
import django_filters
2120
import django_filters.filters
22-
from django_filters.filterset import get_model_field
21+
from django_filters import filterset
2322

2423
from . import filters
2524

2625

27-
class FilterSet(django_filters.FilterSet):
26+
class FilterSetMetaclass(filterset.FilterSetMetaclass):
27+
def __new__(cls, name, bases, attrs):
28+
new_class = super(FilterSetMetaclass, cls).__new__(cls, name, bases, attrs)
29+
30+
# Populate our FilterSet fields with all the possible
31+
# filters for the AllLookupsFilter field.
32+
for name, filter_ in six.iteritems(new_class.base_filters):
33+
if isinstance(filter_, filters.AllLookupsFilter):
34+
model = new_class._meta.model
35+
field = filterset.get_model_field(model, filter_.name)
36+
37+
for lookup_type in django_filters.filters.LOOKUP_TYPES:
38+
if isinstance(field, ForeignObjectRel):
39+
f = new_class.filter_for_reverse_field(field, filter_.name)
40+
else:
41+
f = new_class.filter_for_field(field, filter_.name)
42+
f.lookup_type = lookup_type
43+
f = new_class.fix_filter_field(f)
44+
45+
# compute filter name
46+
filter_name = name
47+
# Don't add "exact" to filter names
48+
if lookup_type != 'exact':
49+
filter_name = LOOKUP_SEP.join([name, lookup_type])
50+
51+
new_class.base_filters[filter_name] = f
52+
53+
return new_class
54+
55+
56+
class FilterSet(six.with_metaclass(FilterSetMetaclass, filterset.FilterSet)):
2857
# In order to support ISO-8601 -- which is the default output for
2958
# DRF -- we need to set up custom date/time input formats.
3059
filter_overrides = {
@@ -39,8 +68,6 @@ class FilterSet(django_filters.FilterSet):
3968
},
4069
}
4170

42-
LOOKUP_TYPES = django_filters.filters.LOOKUP_TYPES
43-
4471
def __init__(self, *args, **kwargs):
4572
super(FilterSet, self).__init__(*args, **kwargs)
4673

@@ -52,20 +79,6 @@ def __init__(self, *args, **kwargs):
5279
isnull_filter = filters.BooleanFilter(name=("%s%sisnull" % (filter_.name, LOOKUP_SEP)))
5380
self.filters['%s%s%s' % (filter_.name, LOOKUP_SEP, 'isnull')] = isnull_filter
5481

55-
elif isinstance(filter_, filters.AllLookupsFilter):
56-
# Populate our FilterSet fields with all the possible
57-
# filters for the AllLookupsFilter field.
58-
model = self._meta.model
59-
field = get_model_field(model, filter_.name)
60-
for lookup_type in self.LOOKUP_TYPES:
61-
if isinstance(field, ForeignObjectRel):
62-
f = self.filter_for_reverse_field(field, filter_.name)
63-
else:
64-
f = self.filter_for_field(field, filter_.name)
65-
f.lookup_type = lookup_type
66-
f = self.fix_filter_field(f)
67-
self.filters["%s%s%s" % (name, LOOKUP_SEP, lookup_type)] = f
68-
6982
def get_filters(self):
7083
"""
7184
Build a set of filters based on the requested data. The resulting set
@@ -122,9 +135,10 @@ def qs(self):
122135

123136
return qs
124137

125-
def fix_filter_field(self, f):
138+
@classmethod
139+
def fix_filter_field(cls, f):
126140
"""
127-
Fix the filter field based on the lookup type.
141+
Fix the filter field based on the lookup type.
128142
"""
129143
lookup_type = f.lookup_type
130144
if lookup_type == 'isnull':

0 commit comments

Comments
 (0)