Commit 98e1a71c authored by Jannis Leidel's avatar Jannis Leidel
Browse files

Fixed #14900 -- Added ability to override the paginator class used in a...

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
parent 7655cd8e
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_ob
from django.contrib import messages
from django.views.decorators.csrf import csrf_protect
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.paginator import Paginator
from django.db import models, transaction, router
from django.db.models.fields import BLANK_CHOICE_DASH
from django.http import Http404, HttpResponse, HttpResponseRedirect
@@ -215,6 +216,7 @@ class ModelAdmin(BaseModelAdmin):
    date_hierarchy = None
    save_as = False
    save_on_top = False
    paginator = Paginator
    inlines = []

    # Custom templates (designed to be over-ridden in subclasses)
@@ -975,8 +977,9 @@ class ModelAdmin(BaseModelAdmin):

        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)
            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.paginator, self)
        except IncorrectLookupParameters:
            # Wacky lookup parameters were given, so redirect to the main
            # changelist page, without parameters, and pass an 'invalid=1'
+6 −2
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ ERROR_FLAG = 'e'
EMPTY_CHANGELIST_VALUE = '(None)'

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.opts = model._meta
        self.lookup_opts = self.opts
@@ -40,6 +40,7 @@ class ChangeList(object):
        self.list_per_page = list_per_page
        self.list_editable = list_editable
        self.model_admin = model_admin
        self.paginator = paginator

        # Get search parameters from the query string.
        try:
@@ -94,7 +95,7 @@ class ChangeList(object):
        return '?%s' % urlencode(p)

    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.
        result_count = paginator.count

@@ -244,3 +245,6 @@ class ChangeList(object):

    def url_for_result(self, result):
        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)
+17 −0
Original line number Diff line number Diff line
@@ -516,6 +516,16 @@ subclass::
        Django will only honor the first element in the list/tuple; any others
        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

    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
    :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
~~~~~~~~~~~~~

+41 −4
Original line number Diff line number Diff line
from django.contrib import admin
from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.views.main import ChangeList
from django.core.paginator import Paginator
from django.template import Context, Template
from django.test import TransactionTestCase

from regressiontests.admin_changelist.models import Child, Parent


class ChangeListTests(TransactionTestCase):
    def test_select_related_preserved(self):
        """
@@ -14,7 +17,8 @@ class ChangeListTests(TransactionTestCase):
        m = ChildAdmin(Child, admin.site)
        cl = ChangeList(MockRequest(), 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)
                m.list_select_related, m.list_per_page, m.list_editable,
                m.paginator, m)
        self.assertEqual(cl.query_set.query.select_related, {'parent': {'name': {}}})

    def test_result_list_html(self):
@@ -28,7 +32,8 @@ class ChangeListTests(TransactionTestCase):
        m = ChildAdmin(Child, admin.site)
        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)
                m.list_select_related, m.list_per_page, m.list_editable,
                m.paginator, m)
        cl.formset = None
        template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
        context = Context({'cl': cl})
@@ -57,7 +62,8 @@ class ChangeListTests(TransactionTestCase):
        m.list_editable = ['name']
        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)
                m.list_select_related, m.list_per_page, m.list_editable,
                m.paginator, m)
        FormSet = m.get_changelist_formset(request)
        cl.formset = FormSet(queryset=cl.result_list)
        template = Template('{% load admin_list %}{% spaceless %}{% result_list cl %}{% endspaceless %}')
@@ -91,7 +97,28 @@ class ChangeListTests(TransactionTestCase):
        self.assertRaises(IncorrectLookupParameters, lambda: \
            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))
                    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):
@@ -99,5 +126,15 @@ class ChildAdmin(admin.ModelAdmin):
    def queryset(self, request):
        return super(ChildAdmin, self).queryset(request).select_related("parent__name")


class MockRequest(object):
    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)