diff --git a/django/core/paginator.py b/django/core/paginator.py index 422da30aa1..2bf858cd54 100644 --- a/django/core/paginator.py +++ b/django/core/paginator.py @@ -6,6 +6,7 @@ from math import ceil from asgiref.sync import sync_to_async +from django.utils.deprecation import RemovedInDjango70Warning from django.utils.functional import cached_property from django.utils.inspect import method_has_no_args from django.utils.translation import gettext_lazy as _ @@ -55,6 +56,18 @@ class BasePaginator: if error_messages is None else self.default_error_messages | error_messages ) + if self.per_page <= self.orphans: + # RemovedInDjango70Warning: When the deprecation ends, replace with: + # raise ValueError( + # "The orphans argument cannot be larger than or equal to the " + # "per_page argument." + # ) + msg = ( + "Support for the orphans argument being larger than or equal to the " + "per_page argument is deprecated. This will raise a ValueError in " + "Django 7.0." + ) + warnings.warn(msg, category=RemovedInDjango70Warning, stacklevel=2) def _check_object_list_is_ordered(self): """ diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 24831951a7..37730ca982 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -31,6 +31,10 @@ details on these changes. * Support for setting the ``ADMINS`` or ``MANAGERS`` settings to a list of (name, address) tuples will be removed. +* The ``orphans`` argument being larger than or equal to the ``per_page`` + argument of ``django.core.paginator.Paginator`` and + ``django.core.paginator.AsyncPaginator`` will no longer be allowed. + .. _deprecation-removed-in-6.1: 6.1 diff --git a/docs/ref/paginator.txt b/docs/ref/paginator.txt index d5b5b0f30e..5acd3d87c2 100644 --- a/docs/ref/paginator.txt +++ b/docs/ref/paginator.txt @@ -48,7 +48,13 @@ For examples, see the :doc:`Pagination topic guide `. themselves. For example, with 23 items, ``per_page=10``, and ``orphans=3``, there will be two pages; the first page with 10 items and the second (and last) page with 13 items. ``orphans`` defaults to zero, which means - pages are never combined and the last page may have one item. + pages are never combined and the last page may have one item. ``orphans`` + should be less than the :attr:`~Paginator.per_page` value. + + .. deprecated:: 6.0 + + Support for the ``orphans`` argument being larger than or equal to the + ``per_page`` argument is deprecated. .. attribute:: Paginator.allow_empty_first_page diff --git a/docs/releases/6.0.txt b/docs/releases/6.0.txt index da69c73044..cd72185354 100644 --- a/docs/releases/6.0.txt +++ b/docs/releases/6.0.txt @@ -336,6 +336,10 @@ Miscellaneous never used the name portion. To include a name, format the address string as ``'"Name"
'`` or use Python's :func:`email.utils.formataddr`. +* Support for the ``orphans`` argument being larger than or equal to the + ``per_page`` argument of :class:`django.core.paginator.Paginator` and + :class:`django.core.paginator.AsyncPaginator` is deprecated. + Features removed in 6.0 ======================= diff --git a/tests/pagination/tests.py b/tests/pagination/tests.py index 67a33cd41d..3e86b88a81 100644 --- a/tests/pagination/tests.py +++ b/tests/pagination/tests.py @@ -15,6 +15,7 @@ from django.core.paginator import ( UnorderedObjectListWarning, ) from django.test import SimpleTestCase, TestCase +from django.utils.deprecation import RemovedInDjango70Warning from .custom import AsyncValidAdjacentNumsPaginator, ValidAdjacentNumsPaginator from .models import Article @@ -82,14 +83,10 @@ class PaginationTests(SimpleTestCase): ((ten, 4, 0, False), (10, 3, [1, 2, 3])), ((ten, 4, 1, False), (10, 3, [1, 2, 3])), ((ten, 4, 2, False), (10, 2, [1, 2])), - ((ten, 4, 5, False), (10, 2, [1, 2])), - ((ten, 4, 6, False), (10, 1, [1])), # Ten items, varying orphans, allow empty first page. ((ten, 4, 0, True), (10, 3, [1, 2, 3])), ((ten, 4, 1, True), (10, 3, [1, 2, 3])), ((ten, 4, 2, True), (10, 2, [1, 2])), - ((ten, 4, 5, True), (10, 2, [1, 2])), - ((ten, 4, 6, True), (10, 1, [1])), # One item, varying orphans, no empty first page. (([1], 4, 0, False), (1, 1, [1])), (([1], 4, 1, False), (1, 1, [1])), @@ -120,7 +117,6 @@ class PaginationTests(SimpleTestCase): (([1, 2, 3], 2, 0, True), (3, 2, [1, 2])), ((eleven, 10, 0, True), (11, 2, [1, 2])), # Number if items one more than per_page with one orphan. - (([1, 2], 1, 1, True), (2, 1, [1])), (([1, 2, 3], 2, 1, True), (3, 1, [1])), ((eleven, 10, 1, True), (11, 1, [1])), # Non-integer inputs @@ -160,6 +156,25 @@ class PaginationTests(SimpleTestCase): with self.assertRaises(InvalidPage): await paginator.apage(3) + def test_orphans_value_larger_than_per_page_value(self): + # RemovedInDjango70Warning: When the deprecation ends, replace with: + # msg = ( + # "The orphans argument cannot be larger than or equal to the " + # "per_page argument." + # ) + msg = ( + "Support for the orphans argument being larger than or equal to the " + "per_page argument is deprecated. This will raise a ValueError in " + "Django 7.0." + ) + for paginator_class in [Paginator, AsyncPaginator]: + for orphans in [2, 3]: + with self.subTest(paginator_class=paginator_class, msg=msg): + # RemovedInDjango70Warning: When the deprecation ends, replace with: + # with self.assertRaisesMessage(ValueError, msg): + with self.assertWarnsMessage(RemovedInDjango70Warning, msg): + paginator_class([1, 2, 3], 2, orphans) + def test_error_messages(self): error_messages = { "invalid_page": "Wrong page number", @@ -331,14 +346,10 @@ class PaginationTests(SimpleTestCase): ((ten, 3, 0, True), (1, 3), (10, 10)), ((ten, 5, 0, True), (1, 5), (6, 10)), # Ten items, varying per_page, with orphans. - ((ten, 1, 1, True), (1, 1), (9, 10)), - ((ten, 1, 2, True), (1, 1), (8, 10)), ((ten, 3, 1, True), (1, 3), (7, 10)), ((ten, 3, 2, True), (1, 3), (7, 10)), - ((ten, 3, 4, True), (1, 3), (4, 10)), ((ten, 5, 1, True), (1, 5), (6, 10)), ((ten, 5, 2, True), (1, 5), (6, 10)), - ((ten, 5, 5, True), (1, 10), (1, 10)), # One item, varying orphans, no empty first page. (([1], 4, 0, False), (1, 1), (1, 1)), (([1], 4, 1, False), (1, 1), (1, 1)),