Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #21241 -- Avoid extraneous JOINs in admin changelist search.

  • Loading branch information...
commit 698dd82eee1cb83f51d4cd39546461bf76976b5e 1 parent 617aceb
Chris Adams authored October 07, 2013 charettes committed October 07, 2013
10  django/contrib/admin/options.py
@@ -849,10 +849,14 @@ def construct_search(field_name):
849 849
         if search_fields and search_term:
850 850
             orm_lookups = [construct_search(str(search_field))
851 851
                            for search_field in search_fields]
  852
+
  853
+            query_parts = []
852 854
             for bit in search_term.split():
853  
-                or_queries = [models.Q(**{orm_lookup: bit})
854  
-                              for orm_lookup in orm_lookups]
855  
-                queryset = queryset.filter(reduce(operator.or_, or_queries))
  855
+                or_queries = (models.Q(**{orm_lookup: bit})
  856
+                              for orm_lookup in orm_lookups)
  857
+                query_parts.append(reduce(operator.or_, or_queries))
  858
+            queryset = queryset.filter(reduce(operator.and_, query_parts))
  859
+
856 860
             if not use_distinct:
857 861
                 for search_spec in orm_lookups:
858 862
                     if lookup_needs_distinct(self.opts, search_spec):
50  tests/admin_changelist/tests.py
@@ -643,6 +643,56 @@ def test_pagination_page_range(self):
643 643
                 list(real_page_range),
644 644
             )
645 645
 
  646
+    def test_search_query_efficiency(self):
  647
+        """Ensure that search queries only add one ORM filter rather than one per term"""
  648
+        new_parent = Parent.objects.create(name='parent')
  649
+        for i in range(200):
  650
+            Child.objects.create(name='foo bar baz qux quux corge %s' % i,
  651
+                                 parent=new_parent)
  652
+
  653
+        m = ParentAdmin(Parent, admin.site)
  654
+
  655
+        request = self.factory.get('/parent/', data={'q': 'foo bar baz'})
  656
+
  657
+        cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
  658
+                        m.list_filter, m.date_hierarchy, m.search_fields,
  659
+                        m.list_select_related, m.list_per_page,
  660
+                        m.list_max_show_all, m.list_editable, m)
  661
+
  662
+        self.assertEqual(2, cl.queryset.query.count_active_tables(),
  663
+                         "ChangeList search filters should not cause duplicate JOINs")
  664
+
  665
+    def test_search_query_logic(self):
  666
+        """Changelist search terms should be ANDed"""
  667
+
  668
+        parent1 = Parent.objects.create(name='parent 1')
  669
+        parent2 = Parent.objects.create(name='parent 2')
  670
+
  671
+        Child.objects.create(name='foo bar baz', parent=parent1)
  672
+        Child.objects.create(name='bar baz qux', parent=parent2)
  673
+
  674
+        m = ParentAdmin(Parent, admin.site)
  675
+
  676
+        request = self.factory.get('/parent/', data={'q': 'foo bar baz'})
  677
+
  678
+        cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
  679
+                        m.list_filter, m.date_hierarchy, m.search_fields,
  680
+                        m.list_select_related, m.list_per_page,
  681
+                        m.list_max_show_all, m.list_editable, m)
  682
+
  683
+        cl.get_results(request)
  684
+        self.assertListEqual(["parent 1"], list(cl.queryset.values_list("name", flat=True)))
  685
+
  686
+
  687
+        request2 = self.factory.get('/parent/', data={'q': 'bar baz'})
  688
+        cl2 = ChangeList(request2, Parent, m.list_display, m.list_display_links,
  689
+                         m.list_filter, m.date_hierarchy, m.search_fields,
  690
+                         m.list_select_related, m.list_per_page,
  691
+                         m.list_max_show_all, m.list_editable, m)
  692
+        cl2.get_results(request2)
  693
+        self.assertListEqual(['parent 1', 'parent 2'],
  694
+                             list(cl2.queryset.order_by("name").values_list("name", flat=True)))
  695
+
646 696
 
647 697
 class AdminLogNodeTestCase(TestCase):
648 698
 

0 notes on commit 698dd82

Please sign in to comment.
Something went wrong with that request. Please try again.