66import warnings
77
88from django .db .models .constants import LOOKUP_SEP
9- from django .db .models .fields .related import ForeignObjectRel
109from django .utils import six
1110
1211from django_filters import filterset , rest_framework
@@ -35,7 +34,7 @@ class FilterSetMetaclass(filterset.FilterSetMetaclass):
3534 def __new__ (cls , name , bases , attrs ):
3635 new_class = super (FilterSetMetaclass , cls ).__new__ (cls , name , bases , attrs )
3736 fix_filter_field = _get_fix_filter_field (new_class )
38- opts = new_class ._meta
37+ opts = copy . deepcopy ( new_class ._meta )
3938
4039 # order_by is not compatible.
4140 if opts .order_by :
@@ -51,38 +50,36 @@ def __new__(cls, name, bases, attrs):
5150 if not opts .model :
5251 return new_class
5352
54- # Populate our FilterSet fields with all the possible
55- # filters for the AllLookupsFilter field.
53+ # Determine declared filters and filters to generate lookups from. Declared
54+ # filters have precedence over generated filters and should not be overwritten.
55+ declared_filters , lookups_filters = OrderedDict (), OrderedDict ()
56+ for name , f in six .iteritems (new_class .declared_filters ):
57+ if isinstance (f , (filters .AllLookupsFilter , filters .RelatedFilter )):
58+ lookups_filters [name ] = f
59+
60+ # `AllLookupsFilter` is an exception, as it should be overwritten
61+ if not isinstance (f , filters .AllLookupsFilter ):
62+ declared_filters [name ] = f
63+
64+ # generate filters for AllLookups/Related filters
65+ # name is the parameter name on the filterset, f.name is the model field's name
66+ for name , f in six .iteritems (lookups_filters ):
67+ opts .fields = {f .name : f .lookups or []}
68+ new_filters = new_class .filters_for_model (opts .model , opts )
69+
70+ # filters_for_model generate param names from the model field name
71+ # replace model field name with the parameter name from the filerset
72+ new_class .base_filters .update (OrderedDict (
73+ (param .replace (f .name , name , 1 ), v )
74+ for param , v in six .iteritems (new_filters )
75+ ))
76+
77+ # re-apply declared filters (sans `AllLookupsFilter`s)
78+ new_class .base_filters .update (declared_filters )
79+
80+ # TODO: remove with deprecations
5681 for name , filter_ in six .iteritems (new_class .base_filters .copy ()):
57- if isinstance (filter_ , (filters .AllLookupsFilter , filters .RelatedFilter )):
58- field = filterset .get_model_field (opts .model , filter_ .name )
59-
60- lookups = filter_ .lookups or []
61- if lookups == '__all__' :
62- lookups = utils .lookups_for_field (field )
63-
64- for lookup_expr in lookups :
65- if isinstance (filter_ , filters .RelatedFilter ) and lookup_expr == 'exact' :
66- # Don't replace the RelatedFilter
67- continue
68-
69- if isinstance (field , ForeignObjectRel ):
70- f = new_class .filter_for_reverse_field (field , filter_ .name )
71- else :
72- f = new_class .filter_for_field (field , filter_ .name , lookup_expr )
73- f = fix_filter_field (f )
74-
75- # compute filter name
76- filter_name = LOOKUP_SEP .join ([name , lookup_expr ])
77-
78- # Don't add "exact" to filter names
79- _exact = LOOKUP_SEP + 'exact'
80- if filter_name .endswith (_exact ):
81- filter_name = filter_name [:- len (_exact )]
82-
83- new_class .base_filters [filter_name ] = f
84-
85- elif name not in new_class .declared_filters :
82+ if name not in new_class .declared_filters :
8683 new_class .base_filters [name ] = fix_filter_field (filter_ )
8784
8885 return new_class
0 commit comments