Index: django/contrib/admin/options.py
===================================================================
--- django/contrib/admin/options.py	(revision 16288)
+++ django/contrib/admin/options.py	(working copy)
@@ -608,6 +608,13 @@
             description = capfirst(action.replace('_', ' '))
         return func, action, description
 
+    def get_list_display(self, request):
+        """
+        Return a sequence containing the fields to be displayed on the
+        changelist.
+        """
+        return self.list_display
+
     def construct_change_message(self, request, form, formsets):
         """
         Construct a change message from a changed object.
@@ -1026,7 +1033,7 @@
         actions = self.get_actions(request)
 
         # Remove action checkboxes if there aren't any actions available.
-        list_display = list(self.list_display)
+        list_display = list(self.get_list_display(request))
         if not actions:
             try:
                 list_display.remove('action_checkbox')
Index: tests/regressiontests/admin_changelist/tests.py
===================================================================
--- tests/regressiontests/admin_changelist/tests.py	(revision 16288)
+++ tests/regressiontests/admin_changelist/tests.py	(working copy)
@@ -1,6 +1,9 @@
+from django.conf import settings
 from django.contrib import admin
 from django.contrib.admin.options import IncorrectLookupParameters
 from django.contrib.admin.views.main import ChangeList
+from django.contrib.auth.models import User
+from django.http import QueryDict
 from django.template import Context, Template
 from django.test import TransactionTestCase
 from regressiontests.admin_changelist.models import Child, Parent
@@ -143,7 +146,40 @@
         self.assertEqual(cl.paginator.count, 30)
         self.assertEqual(cl.paginator.page_range, [1, 2, 3])
 
+    def test_list_display(self):
+        """
+        Regression tests for #14206: dynamic list_display 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)
+
+        # Test with user 'noparents'
+        m = DynamicListDisplayChildAdmin(Child, admin.site)
+        request = ViewMockRequest(user_noparents)
+        response = m.changelist_view(request)
+        self.assertNotContains(response, 'Parent object')
+
+        # Test with user 'parents'
+        m = DynamicListDisplayChildAdmin(Child, admin.site)
+        request = ViewMockRequest(user_parents)
+        response = m.changelist_view(request)
+        self.assertContains(response, 'Parent object')
+
+        # Test default implementation
+        m = ChildAdmin(Child, admin.site)
+        request = ViewMockRequest(user_noparents)
+        response = m.changelist_view(request)
+        self.assertContains(response, 'Parent object')
+
+
 class ChildAdmin(admin.ModelAdmin):
     list_display = ['name', 'parent']
     list_per_page = 10
@@ -157,5 +193,25 @@
         return super(FilteredChildAdmin, self).queryset(request).filter(
             name__contains='filtered')
 
+class DynamicListDisplayChildAdmin(admin.ModelAdmin):
+    list_display = ['name', 'parent']
+
+    def get_list_display(self, request):
+        list_display = self.list_display
+        if request.user.username == 'noparents':
+            list_display.remove('parent')
+
+        return list_display
+
 class MockRequest(object):
     GET = {}
+
+
+class ViewMockRequest(object):
+    def __init__(self, user):
+        self.user = user
+        self.COOKIES = {settings.CSRF_COOKIE_NAME: ''}
+        self.GET = QueryDict('')
+        self.POST = QueryDict('')
+        self.META = {}
+        self.method = 'GET'
Index: docs/ref/contrib/admin/index.txt
===================================================================
--- docs/ref/contrib/admin/index.txt	(revision 16288)
+++ docs/ref/contrib/admin/index.txt	(working copy)
@@ -807,6 +807,15 @@
     a ``list`` or ``tuple`` of field names that will be displayed as read-only,
     as described above in the :attr:`ModelAdmin.readonly_fields` section.
 
+.. method:: ModelAdmin.get_list_display(self, request)
+
+    .. versionadded:: 1.2.6
+
+    The ``get_list_display`` method is given the ``HttpRequest`` 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` section.
+
 .. method:: ModelAdmin.get_urls(self)
 
     .. versionadded:: 1.1
