Index: django/contrib/admin/views/main.py
===================================================================
--- django/contrib/admin/views/main.py	(revision 16061)
+++ django/contrib/admin/views/main.py	(working copy)
@@ -169,6 +169,14 @@
     def get_query_set(self):
         use_distinct = False
 
+        def field_needs_distinct(field):
+            if ((hasattr(field, 'rel') and
+                 isinstance(field.rel, models.ManyToManyRel)) or
+                (isinstance(field, models.related.RelatedObject) and
+                 not field.field.unique)):
+                 return True
+            return 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, TO_FIELD_VAR):
@@ -189,8 +197,7 @@
                     f = self.lookup_opts.get_field_by_name(field_name)[0]
                 except models.FieldDoesNotExist:
                     raise IncorrectLookupParameters
-                if hasattr(f, 'rel') and isinstance(f.rel, models.ManyToManyRel):
-                    use_distinct = True
+                use_distinct = field_needs_distinct(f)
 
             # if key ends with __in, split parameter into separate values
             if key.endswith('__in'):
@@ -264,7 +271,7 @@
                 for search_spec in orm_lookups:
                     field_name = search_spec.split('__', 1)[0]
                     f = self.lookup_opts.get_field_by_name(field_name)[0]
-                    if hasattr(f, 'rel') and isinstance(f.rel, models.ManyToManyRel):
+                    if field_needs_distinct(f):
                         use_distinct = True
                         break
 
Index: tests/regressiontests/admin_changelist/tests.py
===================================================================
--- tests/regressiontests/admin_changelist/tests.py	(revision 16061)
+++ tests/regressiontests/admin_changelist/tests.py	(working copy)
@@ -1,6 +1,6 @@
 from django.contrib import admin
 from django.contrib.admin.options import IncorrectLookupParameters
-from django.contrib.admin.views.main import ChangeList
+from django.contrib.admin.views.main import ChangeList, SEARCH_VAR
 from django.core.paginator import Paginator
 from django.template import Context, Template
 from django.test import TransactionTestCase
@@ -223,6 +223,47 @@
         # There's only one ChordsBand instance
         self.assertEqual(cl.result_count, 1)
 
+    def test_distinct_for_non_unique_related_object_in_list_filter(self):
+        """
+        Regressions tests for #15819: Results shouldn't appear more than
+        once for relationships like a non-unique foreign key.
+        """
+        parent = Parent.objects.create(name='Mary')
+        # Two children with the same name
+        Child.objects.create(parent=parent, name='Daniel')
+        Child.objects.create(parent=parent, name='Daniel')
+
+        m = ParentAdmin(Parent, admin.site)
+
+        request = MockParentFilterRequest('child__name', 'Daniel')
+        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_editable, m)
+
+        # Make sure distinct() was called
+        self.assertEqual(cl.query_set.count(), 1)
+
+    def test_distinct_for_non_unique_related_object_in_search_fields(self):
+        """
+        Regressions tests for #15819: Results shouldn't appear more than
+        once for relationships like a non-unique foreign key.
+        """
+        parent = Parent.objects.create(name='Mary')
+        Child.objects.create(parent=parent, name='Danielle')
+        Child.objects.create(parent=parent, name='Daniel')
+
+        m = ParentAdmin(Parent, admin.site)
+
+        request = MockParentSearchRequest('daniel')
+        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_editable, m)
+
+        # Make sure distinct() was called
+        self.assertEqual(cl.query_set.count(), 1)
+
     def test_pagination(self):
         """
         Regression tests for #12893: Pagination in admins changelist doesn't
@@ -254,6 +295,11 @@
         self.assertEqual(cl.paginator.page_range, [1, 2, 3])
 
 
+class ParentAdmin(admin.ModelAdmin):
+    list_filter = ['child__name']
+    search_fields = ['child__name']
+
+
 class ChildAdmin(admin.ModelAdmin):
     list_display = ['name', 'parent']
     list_per_page = 10
@@ -295,3 +341,11 @@
 class MockFilteredRequestB(object):
     def __init__(self, pk):
         self.GET = { 'members': pk }
+
+class MockParentFilterRequest(object):
+    def __init__(self, filter, q):
+        self.GET = {filter: q}
+
+class MockParentSearchRequest(object):
+    def __init__(self, q):
+        self.GET = {SEARCH_VAR: q}
