mirror of
https://github.com/django/django.git
synced 2025-06-05 03:29:12 +00:00
Fixed #26001 -- Fixed non-string field exact lookups in ModelAdmin.search_fields.
This commit is contained in:
parent
fd82e00697
commit
5b0795ec02
@ -41,6 +41,7 @@ from django.core.exceptions import (
|
|||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.db import models, router, transaction
|
from django.db import models, router, transaction
|
||||||
from django.db.models.constants import LOOKUP_SEP
|
from django.db.models.constants import LOOKUP_SEP
|
||||||
|
from django.db.models.functions import Cast
|
||||||
from django.forms.formsets import DELETION_FIELD_NAME, all_valid
|
from django.forms.formsets import DELETION_FIELD_NAME, all_valid
|
||||||
from django.forms.models import (
|
from django.forms.models import (
|
||||||
BaseInlineFormSet,
|
BaseInlineFormSet,
|
||||||
@ -1207,9 +1208,33 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
may_have_duplicates = False
|
may_have_duplicates = False
|
||||||
search_fields = self.get_search_fields(request)
|
search_fields = self.get_search_fields(request)
|
||||||
if search_fields and search_term:
|
if search_fields and search_term:
|
||||||
orm_lookups = [
|
str_annotations = {}
|
||||||
construct_search(str(search_field)) for search_field in search_fields
|
orm_lookups = []
|
||||||
]
|
for field in search_fields:
|
||||||
|
if field.endswith("__exact"):
|
||||||
|
field_name = field.rsplit("__exact", 1)[0]
|
||||||
|
try:
|
||||||
|
field_obj = queryset.model._meta.get_field(field_name)
|
||||||
|
except FieldDoesNotExist:
|
||||||
|
lookup = construct_search(field)
|
||||||
|
orm_lookups.append(lookup)
|
||||||
|
continue
|
||||||
|
# Add string cast annotations for non-string exact lookups.
|
||||||
|
if not isinstance(field_obj, (models.CharField, models.TextField)):
|
||||||
|
str_annotations[f"{field_name}_str"] = Cast(
|
||||||
|
field_name, output_field=models.CharField()
|
||||||
|
)
|
||||||
|
orm_lookups.append(f"{field_name}_str__exact")
|
||||||
|
else:
|
||||||
|
lookup = construct_search(field)
|
||||||
|
orm_lookups.append(lookup)
|
||||||
|
else:
|
||||||
|
lookup = construct_search(str(field))
|
||||||
|
orm_lookups.append(lookup)
|
||||||
|
|
||||||
|
if str_annotations:
|
||||||
|
queryset = queryset.annotate(**str_annotations)
|
||||||
|
|
||||||
term_queries = []
|
term_queries = []
|
||||||
for bit in smart_split(search_term):
|
for bit in smart_split(search_term):
|
||||||
if bit.startswith(('"', "'")) and bit[0] == bit[-1]:
|
if bit.startswith(('"', "'")) and bit[0] == bit[-1]:
|
||||||
|
@ -48,6 +48,7 @@ class ChildAdmin(admin.ModelAdmin):
|
|||||||
list_display = ["name", "parent"]
|
list_display = ["name", "parent"]
|
||||||
list_per_page = 10
|
list_per_page = 10
|
||||||
list_filter = ["parent", "age"]
|
list_filter = ["parent", "age"]
|
||||||
|
search_fields = ["age__exact", "name__exact"]
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
return super().get_queryset(request).select_related("parent")
|
return super().get_queryset(request).select_related("parent")
|
||||||
|
@ -860,6 +860,25 @@ class ChangeListTests(TestCase):
|
|||||||
cl = m.get_changelist_instance(request)
|
cl = m.get_changelist_instance(request)
|
||||||
self.assertCountEqual(cl.queryset, [abcd])
|
self.assertCountEqual(cl.queryset, [abcd])
|
||||||
|
|
||||||
|
def test_search_with_exact_lookup_for_non_string_field(self):
|
||||||
|
child = Child.objects.create(name="Asher", age=11)
|
||||||
|
model_admin = ChildAdmin(Child, custom_site)
|
||||||
|
|
||||||
|
for search_term, expected_result in [
|
||||||
|
("11", [child]),
|
||||||
|
("Asher", [child]),
|
||||||
|
("1", []),
|
||||||
|
("A", []),
|
||||||
|
("random", []),
|
||||||
|
]:
|
||||||
|
request = self.factory.get("/", data={SEARCH_VAR: search_term})
|
||||||
|
request.user = self.superuser
|
||||||
|
with self.subTest(search_term=search_term):
|
||||||
|
# 1 query for filtered result, 1 for filtered count, 1 for total count.
|
||||||
|
with self.assertNumQueries(3):
|
||||||
|
cl = model_admin.get_changelist_instance(request)
|
||||||
|
self.assertCountEqual(cl.queryset, expected_result)
|
||||||
|
|
||||||
def test_no_distinct_for_m2m_in_list_filter_without_params(self):
|
def test_no_distinct_for_m2m_in_list_filter_without_params(self):
|
||||||
"""
|
"""
|
||||||
If a ManyToManyField is in list_filter but isn't in any lookup params,
|
If a ManyToManyField is in list_filter but isn't in any lookup params,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user