From dfc720c521062cdefd64bc48a11838b0fa583439 Mon Sep 17 00:00:00 2001 From: Marcelo Galigniana Date: Sun, 9 Apr 2023 22:30:40 -0300 Subject: [PATCH] Fixed #27505 -- Allowed customizing Paginator's error messages. --- django/core/paginator.py | 25 +++++++++++++++++++++---- docs/ref/paginator.txt | 36 +++++++++++++++++++++++++++++++++++- docs/releases/5.0.txt | 6 ++++++ tests/pagination/tests.py | 28 ++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 5 deletions(-) diff --git a/django/core/paginator.py b/django/core/paginator.py index 131ea0f811..7b3189cc8b 100644 --- a/django/core/paginator.py +++ b/django/core/paginator.py @@ -28,13 +28,30 @@ class Paginator: # Translators: String used to replace omitted page numbers in elided page # range generated by paginators, e.g. [1, 2, '…', 5, 6, 7, '…', 9, 10]. ELLIPSIS = _("…") + default_error_messages = { + "invalid_page": _("That page number is not an integer"), + "min_page": _("That page number is less than 1"), + "no_results": _("That page contains no results"), + } - def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True): + def __init__( + self, + object_list, + per_page, + orphans=0, + allow_empty_first_page=True, + error_messages=None, + ): self.object_list = object_list self._check_object_list_is_ordered() self.per_page = int(per_page) self.orphans = int(orphans) self.allow_empty_first_page = allow_empty_first_page + self.error_messages = ( + self.default_error_messages + if error_messages is None + else self.default_error_messages | error_messages + ) def __iter__(self): for page_number in self.page_range: @@ -47,11 +64,11 @@ class Paginator: raise ValueError number = int(number) except (TypeError, ValueError): - raise PageNotAnInteger(_("That page number is not an integer")) + raise PageNotAnInteger(self.error_messages["invalid_page"]) if number < 1: - raise EmptyPage(_("That page number is less than 1")) + raise EmptyPage(self.error_messages["min_page"]) if number > self.num_pages: - raise EmptyPage(_("That page contains no results")) + raise EmptyPage(self.error_messages["no_results"]) return number def get_page(self, number): diff --git a/docs/ref/paginator.txt b/docs/ref/paginator.txt index 670c06e6a8..8afadbe6f8 100644 --- a/docs/ref/paginator.txt +++ b/docs/ref/paginator.txt @@ -14,7 +14,7 @@ For examples, see the :doc:`Pagination topic guide `. ``Paginator`` class =================== -.. class:: Paginator(object_list, per_page, orphans=0, allow_empty_first_page=True) +.. class:: Paginator(object_list, per_page, orphans=0, allow_empty_first_page=True, error_messages=None) A paginator acts like a sequence of :class:`Page` when using ``len()`` or iterating it directly. @@ -56,6 +56,40 @@ For examples, see the :doc:`Pagination topic guide `. ``False`` and ``object_list`` is empty, then an ``EmptyPage`` error will be raised. +.. attribute:: Paginator.error_messages + + .. versionadded:: 5.0 + + The ``error_messages`` argument lets you override the default messages that + the paginator will raise. Pass in a dictionary with keys matching the error + messages you want to override. Available error message keys are: + ``invalid_page``, ``min_page``, and ``no_results``. + + For example, here is the default error message: + + .. code-block:: pycon + + >>> from django.core.paginator import Paginator + >>> paginator = Paginator([1, 2, 3], 2) + >>> paginator.page(5) + Traceback (most recent call last): + ... + EmptyPage: That page contains no results + + And here is a custom error message: + + .. code-block:: pycon + + >>> paginator = Paginator( + ... [1, 2, 3], + ... 2, + ... error_messages={"no_results": "Page does not exist"}, + ... ) + >>> paginator.page(5) + Traceback (most recent call last): + ... + EmptyPage: Page does not exist + Methods ------- diff --git a/docs/releases/5.0.txt b/docs/releases/5.0.txt index cc4fb69ee3..0d1e7ffda1 100644 --- a/docs/releases/5.0.txt +++ b/docs/releases/5.0.txt @@ -284,6 +284,12 @@ Models :ref:`Choices classes ` directly instead of requiring expansion with the ``choices`` attribute. +Pagination +~~~~~~~~~~ + +* The new :attr:`django.core.paginator.Paginator.error_messages` argument + allows customizing the error messages raised by :meth:`.Paginator.page`. + Requests and Responses ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/pagination/tests.py b/tests/pagination/tests.py index a04b6dfe8a..1fa9ea2f02 100644 --- a/tests/pagination/tests.py +++ b/tests/pagination/tests.py @@ -128,6 +128,34 @@ class PaginationTests(SimpleTestCase): with self.assertRaises(PageNotAnInteger): paginator.validate_number(1.2) + def test_error_messages(self): + error_messages = { + "invalid_page": "Wrong page number", + "min_page": "Too small", + "no_results": "There is nothing here", + } + paginator = Paginator([1, 2, 3], 2, error_messages=error_messages) + msg = "Wrong page number" + with self.assertRaisesMessage(PageNotAnInteger, msg): + paginator.validate_number(1.2) + msg = "Too small" + with self.assertRaisesMessage(EmptyPage, msg): + paginator.validate_number(-1) + msg = "There is nothing here" + with self.assertRaisesMessage(EmptyPage, msg): + paginator.validate_number(3) + + error_messages = {"min_page": "Too small"} + paginator = Paginator([1, 2, 3], 2, error_messages=error_messages) + # Custom message. + msg = "Too small" + with self.assertRaisesMessage(EmptyPage, msg): + paginator.validate_number(-1) + # Default message. + msg = "That page contains no results" + with self.assertRaisesMessage(EmptyPage, msg): + paginator.validate_number(3) + def test_float_integer_page(self): paginator = Paginator([1, 2, 3], 2) self.assertEqual(paginator.validate_number(1.0), 1)