Index: docs/ref/contrib/admin/index.txt
===================================================================
--- docs/ref/contrib/admin/index.txt	(revision 16381)
+++ docs/ref/contrib/admin/index.txt	(working copy)
@@ -984,6 +984,16 @@
     displayed on the changelist view as described above in the
     :attr:`ModelAdmin.list_display` section.
 
+.. method:: ModelAdmin.get_list_display_links(self, request, list_display)
+
+    .. versionadded:: 1.4
+
+    The ``get_list_display_links`` method is given the ``HttpRequest`` and 
+    the ``list`` or ``tuple`` returned from ModelAdmin.get_list_display
+    and is expected to return a ``list`` or ``tuple`` of field names that
+    will be displayed on the changelist view as described above in the
+    :attr:`ModelAdmin.list_display_links` section.
+
 .. method:: ModelAdmin.get_urls(self)
 
     The ``get_urls`` method on a ``ModelAdmin`` returns the URLs to be used for
Index: django/contrib/admin/options.py
===================================================================
--- django/contrib/admin/options.py	(revision 16381)
+++ django/contrib/admin/options.py	(working copy)
@@ -632,6 +632,13 @@
         """
         return self.list_display
 
+    def get_list_display_links(self, request, list_display):
+        """
+        Return a sequence containing the fields to be displayed as links
+        on the changelist.
+        """
+        return self.list_display_links
+
     def construct_change_message(self, request, form, formsets):
         """
         Construct a change message from a changed object.
@@ -1069,7 +1076,7 @@
 
         ChangeList = self.get_changelist(request)
         try:
-            cl = ChangeList(request, self.model, list_display, self.list_display_links,
+            cl = ChangeList(request, self.model, list_display, list(self.get_list_display_links(request,list_display)),
                 self.list_filter, self.date_hierarchy, self.search_fields,
                 self.list_select_related, self.list_per_page, self.list_editable, self)
         except IncorrectLookupParameters:
Index: tests/regressiontests/admin_changelist/tests.py
===================================================================
--- tests/regressiontests/admin_changelist/tests.py	(revision 16381)
+++ tests/regressiontests/admin_changelist/tests.py	(working copy)
@@ -351,7 +351,52 @@
         response.render()
         self.assertContains(response, 'Parent object')
 
+    def test_dynamic_list_display_links(self):
+        """
+        Regression tests for #16257: dynamic list_display_links support.
+        """
+        parent = Parent.objects.create(name='parent')
+        for i in range(10):
+            Child.objects.create(name='child %s' % i, parent=parent)
 
+        user_noparents = User.objects.create(
+            username='noparents',
+            is_superuser=True)
+        user_parents = User.objects.create(
+            username='parents',
+            is_superuser=True)
+
+        def _mocked_authenticated_request(user):
+            request = self.factory.get('/child/')
+            request.user = user
+            return request
+
+        # Test with user 'noparents'
+        m = DynamicListDisplayLinksChildAdmin(Child, admin.site)
+        request = _mocked_authenticated_request(user_noparents)
+        response = m.changelist_view(request)
+        # XXX - Calling render here to avoid ContentNotRenderedError to be
+        # raised. Ticket #15826 should fix this but it's not yet integrated.
+        response.render()
+        self.assertNotContains(response, 'Parent object')
+
+        # Test with user 'parents'
+        m = DynamicListDisplayLinksChildAdmin(Child, admin.site)
+        request = _mocked_authenticated_request(user_parents)
+        response = m.changelist_view(request)
+        # XXX - #15826
+        response.render()
+        self.assertContains(response, 'Parent object')
+
+        # Test default implementation
+        m = ChildAdmin(Child, admin.site)
+        request = _mocked_authenticated_request(user_noparents)
+        response = m.changelist_view(request)
+        # XXX - #15826
+        response.render()
+        self.assertContains(response, 'Parent object')
+
+
 class ParentAdmin(admin.ModelAdmin):
     list_filter = ['child__name']
     search_fields = ['child__name']
@@ -405,3 +450,21 @@
             my_list_display.remove('parent')
 
         return my_list_display
+
+class DynamicListDisplayLinksChildAdmin(admin.ModelAdmin):
+    list_display = ('name', 'parent')
+    list_display_links = ('parent',)
+
+    def get_list_display(self, request):
+        my_list_display = list(self.list_display)
+        if request.user.username == 'noparents':
+            my_list_display.remove('parent')
+
+        return my_list_display
+
+    def get_list_display_links(self, request, list_display):
+        my_list_display_links = list(set(list_display) & set(self.list_display_links))
+        if my_list_display_links == []:
+            my_list_display_links = [list_display[1]]
+
+        return my_list_display_links
