|
6 | 6 | import warnings
|
7 | 7 |
|
8 | 8 | from django.db.models.constants import LOOKUP_SEP
|
9 |
| -from django.db.models.fields.related import ForeignObjectRel |
10 | 9 | from django.utils import six
|
11 | 10 |
|
12 | 11 | from django_filters import filterset, rest_framework
|
@@ -51,38 +50,37 @@ def __new__(cls, name, bases, attrs):
|
51 | 50 | if not opts.model:
|
52 | 51 | return new_class
|
53 | 52 |
|
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 | + opts = copy.deepcopy(opts) |
| 67 | + for name, f in six.iteritems(lookups_filters): |
| 68 | + opts.fields = {f.name: f.lookups or []} |
| 69 | + new_filters = new_class.filters_for_model(opts.model, opts) |
| 70 | + |
| 71 | + # filters_for_model generate param names from the model field name |
| 72 | + # replace model field name with the parameter name from the filerset |
| 73 | + new_class.base_filters.update(OrderedDict( |
| 74 | + (param.replace(f.name, name, 1), v) |
| 75 | + for param, v in six.iteritems(new_filters) |
| 76 | + )) |
| 77 | + |
| 78 | + # re-apply declared filters (sans `AllLookupsFilter`s) |
| 79 | + new_class.base_filters.update(declared_filters) |
| 80 | + |
| 81 | + # TODO: remove with deprecations |
56 | 82 | 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: |
| 83 | + if name not in new_class.declared_filters: |
86 | 84 | new_class.base_filters[name] = fix_filter_field(filter_)
|
87 | 85 |
|
88 | 86 | return new_class
|
|
0 commit comments