Fixed #14900 -- Added ability to override the paginator class used in a ModelAdmin. Thanks, Adam Vandenberg.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14997 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jannis Leidel 2010-12-21 14:57:29 +00:00
parent 7655cd8eac
commit 98e1a71ceb
4 changed files with 69 additions and 8 deletions

View File

@ -9,6 +9,7 @@ from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_ob
from django.contrib import messages from django.contrib import messages
from django.views.decorators.csrf import csrf_protect from django.views.decorators.csrf import csrf_protect
from django.core.exceptions import PermissionDenied, ValidationError from django.core.exceptions import PermissionDenied, ValidationError
from django.core.paginator import Paginator
from django.db import models, transaction, router from django.db import models, transaction, router
from django.db.models.fields import BLANK_CHOICE_DASH from django.db.models.fields import BLANK_CHOICE_DASH
from django.http import Http404, HttpResponse, HttpResponseRedirect from django.http import Http404, HttpResponse, HttpResponseRedirect
@ -215,6 +216,7 @@ class ModelAdmin(BaseModelAdmin):
date_hierarchy = None date_hierarchy = None
save_as = False save_as = False
save_on_top = False save_on_top = False
paginator = Paginator
inlines = [] inlines = []
# Custom templates (designed to be over-ridden in subclasses) # Custom templates (designed to be over-ridden in subclasses)
@ -975,8 +977,9 @@ class ModelAdmin(BaseModelAdmin):
ChangeList = self.get_changelist(request) ChangeList = self.get_changelist(request)
try: try:
cl = ChangeList(request, self.model, list_display, self.list_display_links, self.list_filter, cl = ChangeList(request, self.model, list_display, self.list_display_links,
self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page, self.list_editable, self) self.list_filter, self.date_hierarchy, self.search_fields,
self.list_select_related, self.list_per_page, self.list_editable, self.paginator, 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

@ -26,7 +26,7 @@ ERROR_FLAG = 'e'
EMPTY_CHANGELIST_VALUE = '(None)' EMPTY_CHANGELIST_VALUE = '(None)'
class ChangeList(object): class ChangeList(object):
def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_editable, model_admin): def __init__(self, request, model, list_display, list_display_links, list_filter, date_hierarchy, search_fields, list_select_related, list_per_page, list_editable, paginator, 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
@ -40,6 +40,7 @@ class ChangeList(object):
self.list_per_page = list_per_page self.list_per_page = list_per_page
self.list_editable = list_editable self.list_editable = list_editable
self.model_admin = model_admin self.model_admin = model_admin
self.paginator = paginator
# Get search parameters from the query string. # Get search parameters from the query string.
try: try:
@ -94,7 +95,7 @@ class ChangeList(object):
return '?%s' % urlencode(p) return '?%s' % urlencode(p)
def get_results(self, request): def get_results(self, request):
paginator = Paginator(self.query_set, self.list_per_page) paginator = self.get_paginator(self.query_set, self.list_per_page)
# Get the number of objects, with admin filters applied. # Get the number of objects, with admin filters applied.
result_count = paginator.count result_count = paginator.count
@ -244,3 +245,6 @@ class ChangeList(object):
def url_for_result(self, result): def url_for_result(self, result):
return "%s/" % quote(getattr(result, self.pk_attname)) return "%s/" % quote(getattr(result, self.pk_attname))
def get_paginator(self, queryset, per_page, orphans=0, allow_empty_first_page=True):
return self.paginator(queryset, per_page, orphans, allow_empty_first_page)

View File

@ -516,6 +516,16 @@ subclass::
Django will only honor the first element in the list/tuple; any others Django will only honor the first element in the list/tuple; any others
will be ignored. will be ignored.
.. attribute:: ModelAdmin.paginator
.. versionadded:: 1.3
The paginator class to be used for pagination. By default,
:class:`django.core.paginator.Paginator` is used. If the custom paginator
class doesn't have the same constructor interface as
:class:`django.core.paginator.Paginator`, you will also need to
provide an implementation for :meth:`MultipleObjectMixin.get_paginator`.
.. attribute:: ModelAdmin.prepopulated_fields .. attribute:: ModelAdmin.prepopulated_fields
Set ``prepopulated_fields`` to a dictionary mapping field names to the Set ``prepopulated_fields`` to a dictionary mapping field names to the
@ -945,6 +955,13 @@ templates used by the :class:`ModelAdmin` views:
using the :mod:`django.contrib.messages` backend. See the using the :mod:`django.contrib.messages` backend. See the
:ref:`custom ModelAdmin example <custom-admin-action>`. :ref:`custom ModelAdmin example <custom-admin-action>`.
.. method:: ModelAdmin.get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True)
.. versionadded:: 1.3
Returns an instance of the paginator to use for this view. By default,
instantiates an instance of :attr:`paginator`.
Other methods Other methods
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View File

@ -1,10 +1,13 @@
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 from django.contrib.admin.views.main import ChangeList
from django.core.paginator import Paginator
from django.template import Context, Template from django.template import Context, Template
from django.test import TransactionTestCase from django.test import TransactionTestCase
from regressiontests.admin_changelist.models import Child, Parent from regressiontests.admin_changelist.models import Child, Parent
class ChangeListTests(TransactionTestCase): class ChangeListTests(TransactionTestCase):
def test_select_related_preserved(self): def test_select_related_preserved(self):
""" """
@ -14,7 +17,8 @@ class ChangeListTests(TransactionTestCase):
m = ChildAdmin(Child, admin.site) m = ChildAdmin(Child, admin.site)
cl = ChangeList(MockRequest(), Child, m.list_display, m.list_display_links, cl = ChangeList(MockRequest(), 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_editable,
m.paginator, 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_html(self): def test_result_list_html(self):
@ -28,7 +32,8 @@ class ChangeListTests(TransactionTestCase):
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_editable,
m.paginator, 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 +62,8 @@ class ChangeListTests(TransactionTestCase):
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_editable,
m.paginator, 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 %}')
@ -91,7 +97,28 @@ class ChangeListTests(TransactionTestCase):
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_editable,
m.paginator, m))
def test_custom_paginator(self):
new_parent = Parent.objects.create(name='parent')
for i in range(200):
new_child = Child.objects.create(name='name %s' % i, parent=new_parent)
request = MockRequest()
m = ChildAdmin(Child, admin.site)
m.list_display = ['id', 'name', 'parent']
m.list_display_links = ['id']
m.list_editable = ['name']
m.paginator = CustomPaginator
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, m.list_editable,
m.paginator, m)
cl.get_results(request)
self.assertIsInstance(cl.paginator, CustomPaginator)
class ChildAdmin(admin.ModelAdmin): class ChildAdmin(admin.ModelAdmin):
@ -99,5 +126,15 @@ class ChildAdmin(admin.ModelAdmin):
def queryset(self, request): def queryset(self, request):
return super(ChildAdmin, self).queryset(request).select_related("parent__name") return super(ChildAdmin, self).queryset(request).select_related("parent__name")
class MockRequest(object): class MockRequest(object):
GET = {} GET = {}
class CustomPaginator(Paginator):
def __init__(self, queryset, page_size, orphans=0, allow_empty_first_page=True):
super(CustomPaginator, self).__init__(
queryset,
5,
orphans=2,
allow_empty_first_page=allow_empty_first_page)