Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add orphans support to MultipleObjectMixin

Fixes #7005
  • Loading branch information...
commit 48e8b5e9446f5f9a0667fccbb0ecbd987a27078b 1 parent fbfa654
@SmileyChris SmileyChris authored
View
19 django/views/generic/list.py
@@ -15,6 +15,7 @@ class MultipleObjectMixin(ContextMixin):
queryset = None
model = None
paginate_by = None
+ paginate_orphans = 0
context_object_name = None
paginator_class = Paginator
page_kwarg = 'page'
@@ -39,7 +40,9 @@ def paginate_queryset(self, queryset, page_size):
"""
Paginate the queryset, if needed.
"""
- paginator = self.get_paginator(queryset, page_size, allow_empty_first_page=self.get_allow_empty())
+ paginator = self.get_paginator(
+ queryset, page_size, orphans=self.get_paginate_orphans(),
+ allow_empty_first_page=self.get_allow_empty())
page_kwarg = self.page_kwarg
page = self.kwargs.get(page_kwarg) or self.request.GET.get(page_kwarg) or 1
try:
@@ -64,11 +67,21 @@ def get_paginate_by(self, queryset):
"""
return self.paginate_by
- def get_paginator(self, queryset, per_page, orphans=0, allow_empty_first_page=True):
+ def get_paginator(self, queryset, per_page, orphans=0,
+ allow_empty_first_page=True, **kwargs):
"""
Return an instance of the paginator for this view.
"""
- return self.paginator_class(queryset, per_page, orphans=orphans, allow_empty_first_page=allow_empty_first_page)
+ return self.paginator_class(
+ queryset, per_page, orphans=orphans,
+ allow_empty_first_page=allow_empty_first_page, **kwargs)
+
+ def get_paginate_orphans(self):
+ """
+ Returns the maximum number of orphans extend the last page by when
+ paginating.
+ """
+ return self.paginate_orphans
def get_allow_empty(self):
"""
View
7 docs/ref/class-based-views/flattened-index.txt
@@ -116,6 +116,7 @@ ListView
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
+* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
@@ -290,6 +291,7 @@ ArchiveIndexView
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
+* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
@@ -325,6 +327,7 @@ YearArchiveView
* :attr:`~django.views.generic.dates.BaseYearArchiveView.make_object_list` [:meth:`~django.views.generic.dates.BaseYearArchiveView.get_make_object_list`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
+* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
@@ -363,6 +366,7 @@ MonthArchiveView
* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`]
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
+* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
@@ -401,6 +405,7 @@ WeekArchiveView
* :attr:`~django.views.generic.base.View.http_method_names`
* :attr:`~django.views.generic.list.MultipleObjectMixin.model`
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
+* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
@@ -443,6 +448,7 @@ DayArchiveView
* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`]
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
+* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
@@ -487,6 +493,7 @@ TodayArchiveView
* :attr:`~django.views.generic.dates.MonthMixin.month` [:meth:`~django.views.generic.dates.MonthMixin.get_month`]
* :attr:`~django.views.generic.dates.MonthMixin.month_format` [:meth:`~django.views.generic.dates.MonthMixin.get_month_format`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_by` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_by`]
+* :attr:`~django.views.generic.list.MultipleObjectMixin.paginate_orphans` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_paginate_orphans`]
* :attr:`~django.views.generic.list.MultipleObjectMixin.paginator_class`
* :attr:`~django.views.generic.list.MultipleObjectMixin.queryset` [:meth:`~django.views.generic.list.MultipleObjectMixin.get_queryset`]
* :attr:`~django.views.generic.base.TemplateResponseMixin.response_class`
View
18 docs/ref/class-based-views/mixins-multiple-object.txt
@@ -72,6 +72,16 @@ MultipleObjectMixin
expect either a ``page`` query string parameter (via ``request.GET``)
or a ``page`` variable specified in the URLconf.
+ .. attribute:: paginate_orphans
+
+ .. versionadded:: 1.6
+
+ An integer specifying the number of "overflow" objects the last page
+ can contain. This extends the :attr:`MultipleObjectMixin.paginate_by`
+ limit on the last page by up to
+ :attr:`MultipleObjectMixin.paginate_orphans`, in order to keep the last
+ page from having a very small number of objects.
+
.. attribute:: page_kwarg
.. versionadded:: 1.5
@@ -119,6 +129,14 @@ MultipleObjectMixin
Returns an instance of the paginator to use for this view. By default,
instantiates an instance of :attr:`paginator_class`.
+ .. method:: get_paginate_by()
+
+ .. versionadded:: 1.6
+
+ An integer specifying the number of "overflow" objects the last page
+ can contain. By default this simply returns the value of
+ :attr:`MultipleObjectMixin.paginate_orphans`.
+
.. method:: get_allow_empty()
Return a boolean specifying whether to display the page if no objects
View
18 tests/regressiontests/generic_views/list.py
@@ -118,8 +118,26 @@ def test_paginated_custom_paginator_constructor(self):
# Custom pagination allows for 2 orphans on a page size of 5
self.assertEqual(len(res.context['object_list']), 7)
+ def test_paginated_orphaned_queryset(self):
+ self._make_authors(92)
+ res = self.client.get('/list/authors/paginated-orphaned/')
+ self.assertEqual(res.status_code, 200)
+ self.assertEqual(res.context['page_obj'].number, 1)
+ res = self.client.get(
+ '/list/authors/paginated-orphaned/', {'page': 'last'})
+ self.assertEqual(res.status_code, 200)
+ self.assertEqual(res.context['page_obj'].number, 3)
+ res = self.client.get(
+ '/list/authors/paginated-orphaned/', {'page': '3'})
+ self.assertEqual(res.status_code, 200)
+ self.assertEqual(res.context['page_obj'].number, 3)
+ res = self.client.get(
+ '/list/authors/paginated-orphaned/', {'page': '4'})
+ self.assertEqual(res.status_code, 404)
+
def test_paginated_non_queryset(self):
res = self.client.get('/list/dict/paginated/')
+
self.assertEqual(res.status_code, 200)
self.assertEqual(len(res.context['object_list']), 1)
View
2  tests/regressiontests/generic_views/urls.py
@@ -133,6 +133,8 @@
views.AuthorList.as_view(paginate_by=30)),
(r'^list/authors/paginated/(?P<page>\d+)/$',
views.AuthorList.as_view(paginate_by=30)),
+ (r'^list/authors/paginated-orphaned/$',
+ views.AuthorList.as_view(paginate_by=30, paginate_orphans=2)),
(r'^list/authors/notempty/$',
views.AuthorList.as_view(allow_empty=False)),
(r'^list/authors/notempty/paginated/$',
Please sign in to comment.
Something went wrong with that request. Please try again.