Fixed #15997 -- Added `list_max_show_all` option to `ModelAdmin` in replacement for a global module level variable. Thanks, jsdalton.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16725 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2011-09-08 13:25:00 +00:00
parent 414c762c3a
commit cf70c96ce0
7 changed files with 85 additions and 23 deletions

View File

@ -278,6 +278,7 @@ class ModelAdmin(BaseModelAdmin):
list_filter = () list_filter = ()
list_select_related = False list_select_related = False
list_per_page = 100 list_per_page = 100
list_max_show_all = 200
list_editable = () list_editable = ()
search_fields = () search_fields = ()
date_hierarchy = None date_hierarchy = None
@ -1087,7 +1088,7 @@ class ModelAdmin(BaseModelAdmin):
try: try:
cl = ChangeList(request, self.model, list_display, self.list_display_links, cl = ChangeList(request, self.model, list_display, self.list_display_links,
self.list_filter, self.date_hierarchy, self.search_fields, self.list_filter, self.date_hierarchy, self.search_fields,
self.list_select_related, self.list_per_page, self.list_editable, self) self.list_select_related, self.list_per_page, self.list_max_show_all, self.list_editable, self)
except IncorrectLookupParameters: except IncorrectLookupParameters:
# Wacky lookup parameters were given, so redirect to the main # Wacky lookup parameters were given, so redirect to the main
# changelist page, without parameters, and pass an 'invalid=1' # changelist page, without parameters, and pass an 'invalid=1'

View File

@ -96,6 +96,11 @@ def validate(cls, model):
raise ImproperlyConfigured("'%s.list_per_page' should be a integer." raise ImproperlyConfigured("'%s.list_per_page' should be a integer."
% cls.__name__) % cls.__name__)
# list_max_show_all
if hasattr(cls, 'list_max_show_all') and not isinstance(cls.list_max_show_all, int):
raise ImproperlyConfigured("'%s.list_max_show_all' should be an integer."
% cls.__name__)
# list_editable # list_editable
if hasattr(cls, 'list_editable') and cls.list_editable: if hasattr(cls, 'list_editable') and cls.list_editable:
check_isseq(cls, 'list_editable', cls.list_editable) check_isseq(cls, 'list_editable', cls.list_editable)

View File

@ -12,10 +12,6 @@ from django.contrib.admin import FieldListFilter
from django.contrib.admin.options import IncorrectLookupParameters from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.util import quote, get_fields_from_path from django.contrib.admin.util import quote, get_fields_from_path
# The system will display a "Show all" link on the change list only if the
# total result count is less than or equal to this setting.
MAX_SHOW_ALL_ALLOWED = 200
# Changelist settings # Changelist settings
ALL_VAR = 'all' ALL_VAR = 'all'
ORDER_VAR = 'o' ORDER_VAR = 'o'
@ -44,7 +40,7 @@ def field_needs_distinct(field):
class ChangeList(object): class ChangeList(object):
def __init__(self, request, model, list_display, list_display_links, def __init__(self, request, model, list_display, list_display_links,
list_filter, date_hierarchy, search_fields, list_select_related, list_filter, date_hierarchy, search_fields, list_select_related,
list_per_page, list_editable, model_admin): list_per_page, list_max_show_all, list_editable, model_admin):
self.model = model self.model = model
self.opts = model._meta self.opts = model._meta
self.lookup_opts = self.opts self.lookup_opts = self.opts
@ -56,6 +52,7 @@ class ChangeList(object):
self.search_fields = search_fields self.search_fields = search_fields
self.list_select_related = list_select_related self.list_select_related = list_select_related
self.list_per_page = list_per_page self.list_per_page = list_per_page
self.list_max_show_all = list_max_show_all
self.model_admin = model_admin self.model_admin = model_admin
# Get search parameters from the query string. # Get search parameters from the query string.
@ -145,7 +142,7 @@ class ChangeList(object):
else: else:
full_result_count = self.root_query_set.count() full_result_count = self.root_query_set.count()
can_show_all = result_count <= MAX_SHOW_ALL_ALLOWED can_show_all = result_count <= self.list_max_show_all
multi_page = result_count > self.list_per_page multi_page = result_count > self.list_per_page
# Get the list of objects to display on this page. # Get the list of objects to display on this page.

View File

@ -695,6 +695,15 @@ subclass::
The ``FieldListFilter`` API is currently considered internal The ``FieldListFilter`` API is currently considered internal
and prone to refactoring. and prone to refactoring.
.. attribute:: ModelAdmin.list_max_show_all
.. versionadded:: 1.4
Set ``list_max_show_all`` to control how many items can appear on a "Show
all" admin change list page. The admin will display a "Show all" link on the
change list only if the total result count is less than or equal to this
setting. By default, this is set to ``200``.
.. attribute:: ModelAdmin.list_per_page .. attribute:: ModelAdmin.list_per_page
Set ``list_per_page`` to control how many items appear on each paginated Set ``list_per_page`` to control how many items appear on each paginated

View File

@ -1,6 +1,8 @@
from __future__ import with_statement
from django.contrib import admin from django.contrib import admin
from django.contrib.admin.options import IncorrectLookupParameters from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.views.main import ChangeList, SEARCH_VAR from django.contrib.admin.views.main import ChangeList, SEARCH_VAR, ALL_VAR
from django.core.paginator import Paginator from django.core.paginator import Paginator
from django.template import Context, Template from django.template import Context, Template
from django.test import TestCase from django.test import TestCase
@ -24,7 +26,7 @@ class ChangeListTests(TestCase):
request = self.factory.get('/child/') request = self.factory.get('/child/')
cl = ChangeList(request, Child, m.list_display, m.list_display_links, cl = ChangeList(request, Child, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields, m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, m.list_editable, m) m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m)
self.assertEqual(cl.query_set.query.select_related, {'parent': {'name': {}}}) self.assertEqual(cl.query_set.query.select_related, {'parent': {'name': {}}})
def test_result_list_empty_changelist_value(self): def test_result_list_empty_changelist_value(self):
@ -37,7 +39,7 @@ class ChangeListTests(TestCase):
m = ChildAdmin(Child, admin.site) m = ChildAdmin(Child, admin.site)
cl = ChangeList(request, Child, m.list_display, m.list_display_links, cl = ChangeList(request, Child, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields, m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, m.list_editable, m) m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m)
cl.formset = None cl.formset = None
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}') template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
context = Context({'cl': cl}) context = Context({'cl': cl})
@ -57,7 +59,7 @@ class ChangeListTests(TestCase):
m = ChildAdmin(Child, admin.site) m = ChildAdmin(Child, admin.site)
cl = ChangeList(request, Child, m.list_display, m.list_display_links, cl = ChangeList(request, Child, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields, m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, m.list_editable, m) m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m)
cl.formset = None cl.formset = None
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}') template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
context = Context({'cl': cl}) context = Context({'cl': cl})
@ -86,7 +88,7 @@ class ChangeListTests(TestCase):
m.list_editable = ['name'] m.list_editable = ['name']
cl = ChangeList(request, Child, m.list_display, m.list_display_links, cl = ChangeList(request, Child, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields, m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, m.list_editable, m) m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m)
FormSet = m.get_changelist_formset(request) FormSet = m.get_changelist_formset(request)
cl.formset = FormSet(queryset=cl.result_list) cl.formset = FormSet(queryset=cl.result_list)
template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}') template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
@ -119,7 +121,7 @@ class ChangeListTests(TestCase):
self.assertRaises(IncorrectLookupParameters, lambda: \ self.assertRaises(IncorrectLookupParameters, lambda: \
ChangeList(request, Child, m.list_display, m.list_display_links, ChangeList(request, Child, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields, m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, m.list_editable, m)) m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m))
def test_custom_paginator(self): def test_custom_paginator(self):
new_parent = Parent.objects.create(name='parent') new_parent = Parent.objects.create(name='parent')
@ -135,7 +137,7 @@ class ChangeListTests(TestCase):
cl = ChangeList(request, Child, m.list_display, m.list_display_links, cl = ChangeList(request, Child, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields, m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, m.list_editable, m) m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m)
cl.get_results(request) cl.get_results(request)
self.assertIsInstance(cl.paginator, CustomPaginator) self.assertIsInstance(cl.paginator, CustomPaginator)
@ -157,7 +159,7 @@ class ChangeListTests(TestCase):
cl = ChangeList(request, Band, m.list_display, cl = ChangeList(request, Band, m.list_display,
m.list_display_links, m.list_filter, m.date_hierarchy, m.list_display_links, m.list_filter, m.date_hierarchy,
m.search_fields, m.list_select_related, m.list_per_page, m.search_fields, m.list_select_related, m.list_per_page,
m.list_editable, m) m.list_max_show_all, m.list_editable, m)
cl.get_results(request) cl.get_results(request)
@ -180,7 +182,7 @@ class ChangeListTests(TestCase):
cl = ChangeList(request, Group, m.list_display, cl = ChangeList(request, Group, m.list_display,
m.list_display_links, m.list_filter, m.date_hierarchy, m.list_display_links, m.list_filter, m.date_hierarchy,
m.search_fields, m.list_select_related, m.list_per_page, m.search_fields, m.list_select_related, m.list_per_page,
m.list_editable, m) m.list_max_show_all, m.list_editable, m)
cl.get_results(request) cl.get_results(request)
@ -204,7 +206,7 @@ class ChangeListTests(TestCase):
cl = ChangeList(request, Quartet, m.list_display, cl = ChangeList(request, Quartet, m.list_display,
m.list_display_links, m.list_filter, m.date_hierarchy, m.list_display_links, m.list_filter, m.date_hierarchy,
m.search_fields, m.list_select_related, m.list_per_page, m.search_fields, m.list_select_related, m.list_per_page,
m.list_editable, m) m.list_max_show_all, m.list_editable, m)
cl.get_results(request) cl.get_results(request)
@ -228,7 +230,7 @@ class ChangeListTests(TestCase):
cl = ChangeList(request, ChordsBand, m.list_display, cl = ChangeList(request, ChordsBand, m.list_display,
m.list_display_links, m.list_filter, m.date_hierarchy, m.list_display_links, m.list_filter, m.date_hierarchy,
m.search_fields, m.list_select_related, m.list_per_page, m.search_fields, m.list_select_related, m.list_per_page,
m.list_editable, m) m.list_max_show_all, m.list_editable, m)
cl.get_results(request) cl.get_results(request)
@ -251,7 +253,7 @@ class ChangeListTests(TestCase):
cl = ChangeList(request, Parent, m.list_display, m.list_display_links, cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields, m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, m.list_select_related, m.list_per_page,
m.list_editable, m) m.list_max_show_all, m.list_editable, m)
# Make sure distinct() was called # Make sure distinct() was called
self.assertEqual(cl.query_set.count(), 1) self.assertEqual(cl.query_set.count(), 1)
@ -271,7 +273,7 @@ class ChangeListTests(TestCase):
cl = ChangeList(request, Parent, m.list_display, m.list_display_links, cl = ChangeList(request, Parent, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields, m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, m.list_select_related, m.list_per_page,
m.list_editable, m) m.list_max_show_all, m.list_editable, m)
# Make sure distinct() was called # Make sure distinct() was called
self.assertEqual(cl.query_set.count(), 1) self.assertEqual(cl.query_set.count(), 1)
@ -292,7 +294,8 @@ class ChangeListTests(TestCase):
m = ChildAdmin(Child, admin.site) m = ChildAdmin(Child, admin.site)
cl = ChangeList(request, Child, m.list_display, m.list_display_links, cl = ChangeList(request, Child, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields, m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, m.list_editable, m) m.list_select_related, m.list_per_page, m.list_max_show_all,
m.list_editable, m)
self.assertEqual(cl.query_set.count(), 60) self.assertEqual(cl.query_set.count(), 60)
self.assertEqual(cl.paginator.count, 60) self.assertEqual(cl.paginator.count, 60)
self.assertEqual(cl.paginator.page_range, [1, 2, 3, 4, 5, 6]) self.assertEqual(cl.paginator.page_range, [1, 2, 3, 4, 5, 6])
@ -301,7 +304,8 @@ class ChangeListTests(TestCase):
m = FilteredChildAdmin(Child, admin.site) m = FilteredChildAdmin(Child, admin.site)
cl = ChangeList(request, Child, m.list_display, m.list_display_links, cl = ChangeList(request, Child, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields, m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, m.list_editable, m) m.list_select_related, m.list_per_page, m.list_max_show_all,
m.list_editable, m)
self.assertEqual(cl.query_set.count(), 30) self.assertEqual(cl.query_set.count(), 30)
self.assertEqual(cl.paginator.count, 30) self.assertEqual(cl.paginator.count, 30)
self.assertEqual(cl.paginator.page_range, [1, 2, 3]) self.assertEqual(cl.paginator.page_range, [1, 2, 3])
@ -351,6 +355,34 @@ class ChangeListTests(TestCase):
response.render() response.render()
self.assertContains(response, 'Parent object') self.assertContains(response, 'Parent object')
def test_show_all(self):
parent = Parent.objects.create(name='anything')
for i in range(30):
Child.objects.create(name='name %s' % i, parent=parent)
Child.objects.create(name='filtered %s' % i, parent=parent)
# Add "show all" parameter to request
request = self.factory.get('/child/', data={ALL_VAR: ''})
# Test valid "show all" request (number of total objects is under max)
m = ChildAdmin(Child, admin.site)
# 200 is the max we'll pass to ChangeList
cl = ChangeList(request, Child, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, 200, m.list_editable, m)
cl.get_results(request)
self.assertEqual(len(cl.result_list), 60)
# Test invalid "show all" request (number of total objects over max)
# falls back to paginated pages
m = ChildAdmin(Child, admin.site)
# 30 is the max we'll pass to ChangeList for this test
cl = ChangeList(request, Child, m.list_display, m.list_display_links,
m.list_filter, m.date_hierarchy, m.search_fields,
m.list_select_related, m.list_per_page, 30, m.list_editable, m)
cl.get_results(request)
self.assertEqual(len(cl.result_list), 10)
class ParentAdmin(admin.ModelAdmin): class ParentAdmin(admin.ModelAdmin):
list_filter = ['child__name'] list_filter = ['child__name']

View File

@ -119,7 +119,7 @@ class ListFiltersTests(TestCase):
def get_changelist(self, request, model, modeladmin): def get_changelist(self, request, model, modeladmin):
return ChangeList(request, model, modeladmin.list_display, modeladmin.list_display_links, return ChangeList(request, model, modeladmin.list_display, modeladmin.list_display_links,
modeladmin.list_filter, modeladmin.date_hierarchy, modeladmin.search_fields, modeladmin.list_filter, modeladmin.date_hierarchy, modeladmin.search_fields,
modeladmin.list_select_related, modeladmin.list_per_page, modeladmin.list_editable, modeladmin) modeladmin.list_select_related, modeladmin.list_per_page, modeladmin.list_max_show_all, modeladmin.list_editable, modeladmin)
def test_datefieldlistfilter(self): def test_datefieldlistfilter(self):
modeladmin = BookAdmin(Book, site) modeladmin = BookAdmin(Book, site)

View File

@ -1115,6 +1115,24 @@ class ValidationTests(unittest.TestCase):
validate(ValidationTestModelAdmin, ValidationTestModel) validate(ValidationTestModelAdmin, ValidationTestModel)
def test_max_show_all_allowed_validation(self):
class ValidationTestModelAdmin(ModelAdmin):
list_max_show_all = 'hello'
self.assertRaisesRegexp(
ImproperlyConfigured,
"'ValidationTestModelAdmin.list_max_show_all' should be an integer.",
validate,
ValidationTestModelAdmin,
ValidationTestModel,
)
class ValidationTestModelAdmin(ModelAdmin):
list_max_show_all = 200
validate(ValidationTestModelAdmin, ValidationTestModel)
def test_search_fields_validation(self): def test_search_fields_validation(self):
class ValidationTestModelAdmin(ModelAdmin): class ValidationTestModelAdmin(ModelAdmin):