diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index c9d4f33884..2b34d7f395 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -351,6 +351,13 @@ class ModelAdmin(BaseModelAdmin): defaults.update(kwargs) return modelform_factory(self.model, **defaults) + def get_changelist(self, request, **kwargs): + """ + Returns the ChangeList class for use on the changelist page. + """ + from django.contrib.admin.views.main import ChangeList + return ChangeList + def get_changelist_form(self, request, **kwargs): """ Returns a Form class for use in the Formset on the changelist page. @@ -888,7 +895,7 @@ class ModelAdmin(BaseModelAdmin): @csrf_protect def changelist_view(self, request, extra_context=None): "The 'change list' admin view for this model." - from django.contrib.admin.views.main import ChangeList, ERROR_FLAG + from django.contrib.admin.views.main import ERROR_FLAG opts = self.model._meta app_label = opts.app_label if not self.has_change_permission(request, None): @@ -905,6 +912,7 @@ class ModelAdmin(BaseModelAdmin): except ValueError: pass + ChangeList = self.get_changelist(request) try: cl = ChangeList(request, self.model, list_display, self.list_display_links, self.list_filter, self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page, self.list_editable, self) diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py index 50bc05eef0..97785c50f9 100644 --- a/tests/regressiontests/admin_views/models.py +++ b/tests/regressiontests/admin_views/models.py @@ -4,6 +4,7 @@ import os from django.core.files.storage import FileSystemStorage from django.db import models from django.contrib import admin +from django.contrib.admin.views.main import ChangeList from django.core.mail import EmailMessage class Section(models.Model): @@ -420,6 +421,20 @@ class CategoryInline(admin.StackedInline): class CollectorAdmin(admin.ModelAdmin): inlines = [WidgetInline, DooHickeyInline, GrommetInline, WhatsitInline, FancyDoodadInline, CategoryInline] +class Gadget(models.Model): + name = models.CharField(max_length=100) + + def __unicode__(self): + return self.name + +class CustomChangeList(ChangeList): + def get_query_set(self): + return self.root_query_set.filter(pk=9999) # Does not exist + +class GadgetAdmin(admin.ModelAdmin): + def get_changelist(self, request, **kwargs): + return CustomChangeList + admin.site.register(Article, ArticleAdmin) admin.site.register(CustomArticle, CustomArticleAdmin) admin.site.register(Section, save_as=True, inlines=[ArticleInline]) @@ -443,6 +458,7 @@ admin.site.register(Recommendation, RecommendationAdmin) admin.site.register(Recommender) admin.site.register(Collector, CollectorAdmin) admin.site.register(Category, CategoryAdmin) +admin.site.register(Gadget, GadgetAdmin) # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. # That way we cover all four cases: diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 3124071503..8e156899a1 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -1203,6 +1203,33 @@ class AdminActionsTest(TestCase): self.failUnlessEqual(Subscriber.objects.count(), 2) +class TestCustomChangeList(TestCase): + fixtures = ['admin-views-users.xml'] + urlbit = 'admin' + + def setUp(self): + result = self.client.login(username='super', password='secret') + self.failUnlessEqual(result, True) + + def tearDown(self): + self.client.logout() + + def test_custom_changelist(self): + """ + Validate that a custom ChangeList class can be used (#9749) + """ + # Insert some data + post_data = {"name": u"First Gadget"} + response = self.client.post('/test_admin/%s/admin_views/gadget/add/' % self.urlbit, post_data) + self.failUnlessEqual(response.status_code, 302) # redirect somewhere + # Hit the page once to get messages out of the queue message list + response = self.client.get('/test_admin/%s/admin_views/gadget/' % self.urlbit) + # Ensure that that data is still not visible on the page + response = self.client.get('/test_admin/%s/admin_views/gadget/' % self.urlbit) + self.failUnlessEqual(response.status_code, 200) + self.assertNotContains(response, 'First Gadget') + + class TestInlineNotEditable(TestCase): fixtures = ['admin-views-users.xml']