diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 8c09c10..2fa9c96 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -164,12 +164,18 @@ class ChangeList(object): return order_field, order_type def get_query_set(self): + DISTINCT = False + qs = self.root_query_set lookup_params = self.params.copy() # a dictionary of the query string for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR): if i in lookup_params: del lookup_params[i] for key, value in lookup_params.items(): + if not DISTINCT and key.count('__') > 1: + # Check if it's a relationship that might return more than one + # instance + DISTINCT = True if not isinstance(key, str): # 'key' will be used as a keyword argument later, so Python # requires it to be a string. @@ -230,16 +236,19 @@ class ChangeList(object): else: return "%s__icontains" % field_name - if self.search_fields and self.query: + if not DISTINCT and self.search_fields and self.query: for bit in self.query.split(): or_queries = [models.Q(**{construct_search(str(field_name)): bit}) for field_name in self.search_fields] qs = qs.filter(reduce(operator.or_, or_queries)) for field_name in self.search_fields: if '__' in field_name: - qs = qs.distinct() + DISCTINCT = True break - return qs + if DISTINCT: + return qs.distinct() + else: + return qs def url_for_result(self, result): return "%s/" % quote(getattr(result, self.pk_attname)) diff --git a/tests/regressiontests/admin_changelist/models.py b/tests/regressiontests/admin_changelist/models.py index f030a78..c043c8a 100644 --- a/tests/regressiontests/admin_changelist/models.py +++ b/tests/regressiontests/admin_changelist/models.py @@ -6,4 +6,10 @@ class Parent(models.Model): class Child(models.Model): parent = models.ForeignKey(Parent, editable=False) - name = models.CharField(max_length=30, blank=True) \ No newline at end of file + name = models.CharField(max_length=30, blank=True) + multiple_m2m = models.ManyToManyField(Parent, related_name='multiple_m2m', + through='Through') + +class Through(models.Model): + parent = models.ForeignKey(Parent) + child = models.ForeignKey(Child) diff --git a/tests/regressiontests/admin_changelist/tests.py b/tests/regressiontests/admin_changelist/tests.py index b70d7c5..569bfe8 100644 --- a/tests/regressiontests/admin_changelist/tests.py +++ b/tests/regressiontests/admin_changelist/tests.py @@ -2,9 +2,17 @@ import unittest from django.contrib import admin from django.contrib.admin.views.main import ChangeList from django.template import Context, Template -from regressiontests.admin_changelist.models import Child, Parent +from regressiontests.admin_changelist.models import Child, Parent, Through class ChangeListTests(unittest.TestCase): + def setUp(self): + self.new_parent = Parent.objects.create(name='parent') + self.new_child = Child.objects.create(name='name', parent=self.new_parent) + + def tearDown(self): + self.new_parent.delete() + self.new_child.delete() + def test_select_related_preserved(self): """ Regression test for #10348: ChangeList.get_query_set() shouldn't @@ -22,8 +30,8 @@ class ChangeListTests(unittest.TestCase): table and this checks that the items are nested within the table element tags. """ - new_parent = Parent.objects.create(name='parent') - new_child = Child.objects.create(name='name', parent=new_parent) + new_parent = self.new_parent + new_child = self.new_child request = MockRequest() m = ChildAdmin(Child, admin.site) cl = ChangeList(request, Child, m.list_display, m.list_display_links, @@ -57,10 +65,35 @@ class ChangeListTests(unittest.TestCase): self.failIf(table_output.find('