diff options
| author | Krzysztof Nazarewski <nazarewk@gmail.com> | 2017-07-05 13:00:10 +0200 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2017-11-18 19:33:52 -0500 |
| commit | 244cc401559e924355cf943b6b8e66ccf2f6da3a (patch) | |
| tree | 124c16b3fd1e844cb2925fc4fd0c76b7fde0584f /django | |
| parent | 3af305e8b8a89f4b0e5874cd601568ab8dcd7334 (diff) | |
Fixed #26184 -- Allowed using any lookups in ModelAdmin.search_fields.
Thanks Krzysztof Nazarewski for the initial patch.
Diffstat (limited to 'django')
| -rw-r--r-- | django/contrib/admin/options.py | 23 | ||||
| -rw-r--r-- | django/contrib/admin/utils.py | 27 | ||||
| -rw-r--r-- | django/db/models/sql/constants.py | 10 |
3 files changed, 35 insertions, 25 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 545c8a53cd..627c76be6a 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -943,8 +943,27 @@ class ModelAdmin(BaseModelAdmin): return "%s__iexact" % field_name[1:] elif field_name.startswith('@'): return "%s__search" % field_name[1:] - else: - return "%s__icontains" % field_name + # Use field_name if it includes a lookup. + opts = queryset.model._meta + lookup_fields = field_name.split(LOOKUP_SEP) + # Go through the fields, following all relations. + prev_field = None + for path_part in lookup_fields: + if path_part == 'pk': + path_part = opts.pk.name + try: + field = opts.get_field(path_part) + except FieldDoesNotExist: + # Use valid query lookups. + if prev_field and prev_field.get_lookup(path_part): + return field_name + else: + prev_field = field + if hasattr(field, 'get_path_info'): + # Update opts to follow the relation. + opts = field.get_path_info()[-1].to_opts + # Otherwise, use the field with icontains. + return "%s__icontains" % field_name use_distinct = False search_fields = self.get_search_fields(request) diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py index c55336ad00..f1e2d4ed87 100644 --- a/django/contrib/admin/utils.py +++ b/django/contrib/admin/utils.py @@ -7,7 +7,6 @@ from django.core.exceptions import FieldDoesNotExist from django.db import models from django.db.models.constants import LOOKUP_SEP from django.db.models.deletion import Collector -from django.db.models.sql.constants import QUERY_TERMS from django.forms.utils import pretty_name from django.urls import NoReverseMatch, reverse from django.utils import formats, timezone @@ -26,21 +25,23 @@ def lookup_needs_distinct(opts, lookup_path): Return True if 'distinct()' should be used to query the given lookup path. """ lookup_fields = lookup_path.split(LOOKUP_SEP) - # Remove the last item of the lookup path if it is a query term - if lookup_fields[-1] in QUERY_TERMS: - lookup_fields = lookup_fields[:-1] - # Now go through the fields (following all relations) and look for an m2m + # Go through the fields (following all relations) and look for an m2m. for field_name in lookup_fields: if field_name == 'pk': field_name = opts.pk.name - field = opts.get_field(field_name) - if hasattr(field, 'get_path_info'): - # This field is a relation, update opts to follow the relation - path_info = field.get_path_info() - opts = path_info[-1].to_opts - if any(path.m2m for path in path_info): - # This field is a m2m relation so we know we need to call distinct - return True + try: + field = opts.get_field(field_name) + except FieldDoesNotExist: + # Ignore query lookups. + continue + else: + if hasattr(field, 'get_path_info'): + # This field is a relation; update opts to follow the relation. + path_info = field.get_path_info() + opts = path_info[-1].to_opts + if any(path.m2m for path in path_info): + # This field is a m2m relation so distinct must be called. + return True return False diff --git a/django/db/models/sql/constants.py b/django/db/models/sql/constants.py index 57857796b8..28f4242a7a 100644 --- a/django/db/models/sql/constants.py +++ b/django/db/models/sql/constants.py @@ -4,16 +4,6 @@ Constants specific to the SQL storage portion of the ORM. import re -# Valid query types (a set is used for speedy lookups). These are (currently) -# considered SQL-specific; other storage systems may choose to use different -# lookup types. -QUERY_TERMS = { - 'exact', 'iexact', 'contains', 'icontains', 'gt', 'gte', 'lt', 'lte', 'in', - 'startswith', 'istartswith', 'endswith', 'iendswith', 'range', 'year', - 'month', 'day', 'week_day', 'hour', 'minute', 'second', 'isnull', 'search', - 'regex', 'iregex', -} - # Size of each "chunk" for get_iterator calls. # Larger values are slightly faster at the expense of more storage space. GET_ITERATOR_CHUNK_SIZE = 100 |
