Ticket #15819: non-unique-related-fields-distinct1.diff

File non-unique-related-fields-distinct1.diff, 5.0 KB (added by Ryan Kaskel, 8 years ago)
  • django/contrib/admin/views/main.py

     
    169169    def get_query_set(self):
    170170        use_distinct = False
    171171
     172        def field_needs_distinct(field):
     173            if ((hasattr(field, 'rel') and
     174                 isinstance(field.rel, models.ManyToManyRel)) or
     175                (isinstance(field, models.related.RelatedObject) and
     176                 not field.field.unique)):
     177                 return True
     178            return False
     179
    172180        qs = self.root_query_set
    173181        lookup_params = self.params.copy() # a dictionary of the query string
    174182        for i in (ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR, TO_FIELD_VAR):
     
    189197                    f = self.lookup_opts.get_field_by_name(field_name)[0]
    190198                except models.FieldDoesNotExist:
    191199                    raise IncorrectLookupParameters
    192                 if hasattr(f, 'rel') and isinstance(f.rel, models.ManyToManyRel):
    193                     use_distinct = True
     200                use_distinct = field_needs_distinct(f)
    194201
    195202            # if key ends with __in, split parameter into separate values
    196203            if key.endswith('__in'):
     
    264271                for search_spec in orm_lookups:
    265272                    field_name = search_spec.split('__', 1)[0]
    266273                    f = self.lookup_opts.get_field_by_name(field_name)[0]
    267                     if hasattr(f, 'rel') and isinstance(f.rel, models.ManyToManyRel):
     274                    if field_needs_distinct(f):
    268275                        use_distinct = True
    269276                        break
    270277
  • tests/regressiontests/admin_changelist/tests.py

     
    11from django.contrib import admin
    22from django.contrib.admin.options import IncorrectLookupParameters
    3 from django.contrib.admin.views.main import ChangeList
     3from django.contrib.admin.views.main import ChangeList, SEARCH_VAR
    44from django.core.paginator import Paginator
    55from django.template import Context, Template
    66from django.test import TransactionTestCase
     
    223223        # There's only one ChordsBand instance
    224224        self.assertEqual(cl.result_count, 1)
    225225
     226    def test_distinct_for_non_unique_related_object_in_list_filter(self):
     227        """
     228        Regressions tests for #15819: Results shouldn't appear more than
     229        once for relationships like a non-unique foreign key.
     230        """
     231        parent = Parent.objects.create(name='Mary')
     232        # Two children with the same name
     233        Child.objects.create(parent=parent, name='Daniel')
     234        Child.objects.create(parent=parent, name='Daniel')
     235
     236        m = ParentAdmin(Parent, admin.site)
     237
     238        request = MockParentFilterRequest('child__name', 'Daniel')
     239        cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
     240                        m.list_filter, m.date_hierarchy, m.search_fields,
     241                        m.list_select_related, m.list_per_page,
     242                        m.list_editable, m)
     243
     244        # Make sure distinct() was called
     245        self.assertEqual(cl.query_set.count(), 1)
     246
     247    def test_distinct_for_non_unique_related_object_in_search_fields(self):
     248        """
     249        Regressions tests for #15819: Results shouldn't appear more than
     250        once for relationships like a non-unique foreign key.
     251        """
     252        parent = Parent.objects.create(name='Mary')
     253        Child.objects.create(parent=parent, name='Danielle')
     254        Child.objects.create(parent=parent, name='Daniel')
     255
     256        m = ParentAdmin(Parent, admin.site)
     257
     258        request = MockParentSearchRequest('daniel')
     259        cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
     260                        m.list_filter, m.date_hierarchy, m.search_fields,
     261                        m.list_select_related, m.list_per_page,
     262                        m.list_editable, m)
     263
     264        # Make sure distinct() was called
     265        self.assertEqual(cl.query_set.count(), 1)
     266
    226267    def test_pagination(self):
    227268        """
    228269        Regression tests for #12893: Pagination in admins changelist doesn't
     
    254295        self.assertEqual(cl.paginator.page_range, [1, 2, 3])
    255296
    256297
     298class ParentAdmin(admin.ModelAdmin):
     299    list_filter = ['child__name']
     300    search_fields = ['child__name']
     301
     302
    257303class ChildAdmin(admin.ModelAdmin):
    258304    list_display = ['name', 'parent']
    259305    list_per_page = 10
     
    295341class MockFilteredRequestB(object):
    296342    def __init__(self, pk):
    297343        self.GET = { 'members': pk }
     344
     345class MockParentFilterRequest(object):
     346    def __init__(self, filter, q):
     347        self.GET = {filter: q}
     348
     349class MockParentSearchRequest(object):
     350    def __init__(self, q):
     351        self.GET = {SEARCH_VAR: q}
Back to Top