1010from django .db .models .fields .related import ForeignObjectRel
1111from django .utils import six
1212
13- import django_filters
14- import django_filters .filters
1513from django_filters import filterset
1614
1715from . import filters
16+ from . import utils
17+
18+
19+ def _base (f ):
20+ f ._base = True
21+ return f
22+
23+
24+ def _get_fix_filter_field (cls ):
25+ method = getattr (cls , 'fix_filter_field' )
26+ if not getattr (method , '_base' , False ):
27+ warnings .warn (
28+ 'fix_filter_field is deprecated and no longer necessary. See: '
29+ 'https://github.com/philipn/django-rest-framework-filters/issues/62' ,
30+ DeprecationWarning , stacklevel = 2
31+ )
32+ return cls .fix_filter_field
1833
1934
2035class FilterSetMetaclass (filterset .FilterSetMetaclass ):
2136 def __new__ (cls , name , bases , attrs ):
37+ cls .convert__all__ (attrs )
38+
2239 new_class = super (FilterSetMetaclass , cls ).__new__ (cls , name , bases , attrs )
40+ fix_filter_field = _get_fix_filter_field (new_class )
2341
2442 # Populate our FilterSet fields with all the possible
2543 # filters for the AllLookupsFilter field.
@@ -28,24 +46,25 @@ def __new__(cls, name, bases, attrs):
2846 model = new_class ._meta .model
2947 field = filterset .get_model_field (model , filter_ .name )
3048
31- for lookup_type in django_filters . filters . LOOKUP_TYPES :
49+ for lookup_expr in utils . lookups_for_field ( field ) :
3250 if isinstance (field , ForeignObjectRel ):
3351 f = new_class .filter_for_reverse_field (field , filter_ .name )
3452 else :
35- f = new_class .filter_for_field (field , filter_ .name )
36- f .lookup_type = lookup_type
37- f = new_class .fix_filter_field (f )
53+ f = new_class .filter_for_field (field , filter_ .name , lookup_expr )
54+ f = fix_filter_field (f )
3855
3956 # compute filter name
40- filter_name = name
57+ filter_name = LOOKUP_SEP .join ([name , lookup_expr ])
58+
4159 # Don't add "exact" to filter names
42- if lookup_type != 'exact' :
43- filter_name = LOOKUP_SEP .join ([name , lookup_type ])
60+ _exact = LOOKUP_SEP + 'exact'
61+ if filter_name .endswith (_exact ):
62+ filter_name = filter_name [:- len (_exact )]
4463
4564 new_class .base_filters [filter_name ] = f
4665
4766 elif name not in new_class .declared_filters :
48- new_class .base_filters [name ] = new_class . fix_filter_field (filter_ )
67+ new_class .base_filters [name ] = fix_filter_field (filter_ )
4968
5069 return new_class
5170
@@ -61,9 +80,33 @@ def related_filters(self):
6180 ])
6281 return self ._related_filters
6382
83+ @staticmethod
84+ def convert__all__ (attrs ):
85+ """
86+ Extract Meta.fields and convert any fields w/ `__all__`
87+ to a declared AllLookupsFilter.
88+ """
89+ meta = attrs .get ('Meta' , None )
90+ fields = getattr (meta , 'fields' , None )
91+
92+ if isinstance (fields , dict ):
93+ for name , lookups in six .iteritems (fields .copy ()):
94+ if lookups == filters .ALL_LOOKUPS :
95+ warnings .warn (
96+ "ALL_LOOKUPS has been deprecated in favor of '__all__'. See: "
97+ "https://github.com/philipn/django-rest-framework-filters/issues/62" ,
98+ DeprecationWarning , stacklevel = 2
99+ )
100+ lookups = '__all__'
101+
102+ if lookups == '__all__' :
103+ del fields [name ]
104+ attrs [name ] = filters .AllLookupsFilter ()
105+
64106
65107class FilterSet (six .with_metaclass (FilterSetMetaclass , filterset .FilterSet )):
66108 filter_overrides = {
109+ # uses API-friendly django_filters.BooleanWidget
67110 models .BooleanField : {
68111 'filter_class' : filters .BooleanFilter ,
69112 },
@@ -91,7 +134,7 @@ def __init__(self, *args, **kwargs):
91134 # Add an 'isnull' filter to allow checking if the relation is empty.
92135 filter_name = "%s%sisnull" % (filter_ .name , LOOKUP_SEP )
93136 if filter_name not in self .filters :
94- self .filters [filter_name ] = filters .BooleanFilter (name = filter_ .name , lookup_type = 'isnull' )
137+ self .filters [filter_name ] = filters .BooleanFilter (name = filter_ .name , lookup_expr = 'isnull' )
95138
96139 elif isinstance (filter_ , filters .MethodFilter ):
97140 filter_ .resolve_action ()
@@ -276,15 +319,6 @@ def qs(self):
276319 return qs
277320
278321 @classmethod
322+ @_base
279323 def fix_filter_field (cls , f ):
280- """
281- Fix the filter field based on the lookup type.
282- """
283- lookup_type = f .lookup_type
284- if lookup_type == 'isnull' :
285- return filters .BooleanFilter (name = f .name , lookup_type = 'isnull' )
286- if lookup_type == 'in' and type (f ) == filters .NumberFilter :
287- return filters .InSetNumberFilter (name = f .name , lookup_type = 'in' )
288- if lookup_type == 'in' and type (f ) == filters .CharFilter :
289- return filters .InSetCharFilter (name = f .name , lookup_type = 'in' )
290324 return f
0 commit comments