Browse Source

Fixed #28032 -- Added Paginator.get_page().

Moved boilerplate from docs to a method.
Sami J. Lehtinen 8 years ago
parent
commit
407c1249c9
4 changed files with 66 additions and 9 deletions
  1. 13 0
      django/core/paginator.py
  2. 6 0
      docs/releases/2.0.txt
  3. 15 9
      docs/topics/pagination.txt
  4. 32 0
      tests/pagination/tests.py

+ 13 - 0
django/core/paginator.py

@@ -47,6 +47,19 @@ class Paginator:
                 raise EmptyPage(_('That page contains no results'))
         return number
 
+    def get_page(self, number):
+        """
+        Return a valid page, even if the page argument isn't a number or isn't
+        in range.
+        """
+        try:
+            number = self.validate_number(number)
+        except PageNotAnInteger:
+            number = 1
+        except EmptyPage:
+            number = self.num_pages
+        return self.page(number)
+
     def page(self, number):
         """Return a Page object for the given 1-based page number."""
         number = self.validate_number(number)

+ 6 - 0
docs/releases/2.0.txt

@@ -290,6 +290,12 @@ Models
 * Added support for expressions in :attr:`Meta.ordering
   <django.db.models.Options.ordering>`.
 
+Pagination
+~~~~~~~~~~
+
+* Added :meth:`Paginator.get_page() <django.core.paginator.Paginator.get_page>`
+  to provide the documented pattern of handling invalid page numbers.
+
 Requests and Responses
 ~~~~~~~~~~~~~~~~~~~~~~
 

+ 15 - 9
docs/topics/pagination.txt

@@ -93,15 +93,7 @@ The view function looks like this::
         paginator = Paginator(contact_list, 25) # Show 25 contacts per page
 
         page = request.GET.get('page')
-        try:
-            contacts = paginator.page(page)
-        except PageNotAnInteger:
-            # If page is not an integer, deliver first page.
-            contacts = paginator.page(1)
-        except EmptyPage:
-            # If page is out of range (e.g. 9999), deliver last page of results.
-            contacts = paginator.page(paginator.num_pages)
-
+        contacts = paginator.get_page(page)
         return render(request, 'list.html', {'contacts': contacts})
 
 In the template :file:`list.html`, you'll want to include navigation between
@@ -177,6 +169,20 @@ Optional arguments
 Methods
 -------
 
+.. method:: Paginator.get_page(number)
+
+    .. versionadded:: 2.0
+
+    Returns a :class:`Page` object with the given 1-based index, while also
+    handling out of range and invalid page numbers.
+
+    If the page isn't a number, it returns the first page. If the page number
+    is negative or greater than the number of pages, it returns the last page.
+
+    It raises an exception (:exc:`EmptyPage`) only if you specify
+    ``Paginator(..., allow_empty_first_page=False)`` and the ``object_list`` is
+    empty.
+
 .. method:: Paginator.page(number)
 
     Returns a :class:`Page` object with the given 1-based index. Raises

+ 32 - 0
tests/pagination/tests.py

@@ -242,6 +242,38 @@ class PaginationTests(unittest.TestCase):
         """
         self.assertIsInstance(Paginator([1, 2, 3], 2).page_range, type(range(0)))
 
+    def test_get_page(self):
+        """
+        Paginator.get_page() returns a valid page even with invalid page
+        arguments.
+        """
+        paginator = Paginator([1, 2, 3], 2)
+        page = paginator.get_page(1)
+        self.assertEqual(page.number, 1)
+        self.assertEqual(page.object_list, [1, 2])
+        # An empty page returns the last page.
+        self.assertEqual(paginator.get_page(3).number, 2)
+        # Non-integer page returns the first page.
+        self.assertEqual(paginator.get_page(None).number, 1)
+
+    def test_get_page_empty_object_list(self):
+        """Paginator.get_page() with an empty object_list."""
+        paginator = Paginator([], 2)
+        # An empty page returns the last page.
+        self.assertEqual(paginator.get_page(1).number, 1)
+        self.assertEqual(paginator.get_page(2).number, 1)
+        # Non-integer page returns the first page.
+        self.assertEqual(paginator.get_page(None).number, 1)
+
+    def test_get_page_empty_object_list_and_allow_empty_first_page_false(self):
+        """
+        Paginator.get_page() raises EmptyPage if allow_empty_first_page=False
+        and object_list is empty.
+        """
+        paginator = Paginator([], 2, allow_empty_first_page=False)
+        with self.assertRaises(EmptyPage):
+            paginator.get_page(1)
+
 
 class ModelPaginationTests(TestCase):
     """