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:
parent
414c762c3a
commit
cf70c96ce0
|
@ -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'
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue