mirror of
https://github.com/django/django.git
synced 2025-03-26 09:10:50 +00:00
Fixed #21241 -- Avoid extraneous JOINs in admin changelist search.
This commit is contained in:
parent
617aceb1b4
commit
698dd82eee
@ -849,10 +849,14 @@ class ModelAdmin(BaseModelAdmin):
|
|||||||
if search_fields and search_term:
|
if search_fields and search_term:
|
||||||
orm_lookups = [construct_search(str(search_field))
|
orm_lookups = [construct_search(str(search_field))
|
||||||
for search_field in search_fields]
|
for search_field in search_fields]
|
||||||
|
|
||||||
|
query_parts = []
|
||||||
for bit in search_term.split():
|
for bit in search_term.split():
|
||||||
or_queries = [models.Q(**{orm_lookup: bit})
|
or_queries = (models.Q(**{orm_lookup: bit})
|
||||||
for orm_lookup in orm_lookups]
|
for orm_lookup in orm_lookups)
|
||||||
queryset = queryset.filter(reduce(operator.or_, or_queries))
|
query_parts.append(reduce(operator.or_, or_queries))
|
||||||
|
queryset = queryset.filter(reduce(operator.and_, query_parts))
|
||||||
|
|
||||||
if not use_distinct:
|
if not use_distinct:
|
||||||
for search_spec in orm_lookups:
|
for search_spec in orm_lookups:
|
||||||
if lookup_needs_distinct(self.opts, search_spec):
|
if lookup_needs_distinct(self.opts, search_spec):
|
||||||
|
@ -643,6 +643,56 @@ class ChangeListTests(TestCase):
|
|||||||
list(real_page_range),
|
list(real_page_range),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_search_query_efficiency(self):
|
||||||
|
"""Ensure that search queries only add one ORM filter rather than one per term"""
|
||||||
|
new_parent = Parent.objects.create(name='parent')
|
||||||
|
for i in range(200):
|
||||||
|
Child.objects.create(name='foo bar baz qux quux corge %s' % i,
|
||||||
|
parent=new_parent)
|
||||||
|
|
||||||
|
m = ParentAdmin(Parent, admin.site)
|
||||||
|
|
||||||
|
request = self.factory.get('/parent/', data={'q': 'foo bar baz'})
|
||||||
|
|
||||||
|
cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
|
||||||
|
m.list_filter, m.date_hierarchy, m.search_fields,
|
||||||
|
m.list_select_related, m.list_per_page,
|
||||||
|
m.list_max_show_all, m.list_editable, m)
|
||||||
|
|
||||||
|
self.assertEqual(2, cl.queryset.query.count_active_tables(),
|
||||||
|
"ChangeList search filters should not cause duplicate JOINs")
|
||||||
|
|
||||||
|
def test_search_query_logic(self):
|
||||||
|
"""Changelist search terms should be ANDed"""
|
||||||
|
|
||||||
|
parent1 = Parent.objects.create(name='parent 1')
|
||||||
|
parent2 = Parent.objects.create(name='parent 2')
|
||||||
|
|
||||||
|
Child.objects.create(name='foo bar baz', parent=parent1)
|
||||||
|
Child.objects.create(name='bar baz qux', parent=parent2)
|
||||||
|
|
||||||
|
m = ParentAdmin(Parent, admin.site)
|
||||||
|
|
||||||
|
request = self.factory.get('/parent/', data={'q': 'foo bar baz'})
|
||||||
|
|
||||||
|
cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
|
||||||
|
m.list_filter, m.date_hierarchy, m.search_fields,
|
||||||
|
m.list_select_related, m.list_per_page,
|
||||||
|
m.list_max_show_all, m.list_editable, m)
|
||||||
|
|
||||||
|
cl.get_results(request)
|
||||||
|
self.assertListEqual(["parent 1"], list(cl.queryset.values_list("name", flat=True)))
|
||||||
|
|
||||||
|
|
||||||
|
request2 = self.factory.get('/parent/', data={'q': 'bar baz'})
|
||||||
|
cl2 = ChangeList(request2, Parent, m.list_display, m.list_display_links,
|
||||||
|
m.list_filter, m.date_hierarchy, m.search_fields,
|
||||||
|
m.list_select_related, m.list_per_page,
|
||||||
|
m.list_max_show_all, m.list_editable, m)
|
||||||
|
cl2.get_results(request2)
|
||||||
|
self.assertListEqual(['parent 1', 'parent 2'],
|
||||||
|
list(cl2.queryset.order_by("name").values_list("name", flat=True)))
|
||||||
|
|
||||||
|
|
||||||
class AdminLogNodeTestCase(TestCase):
|
class AdminLogNodeTestCase(TestCase):
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user