Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixes #11596 -- Make paginator.Page iterable

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16018 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 4fa96467164d738c6f8a9c59423382257f95b747 1 parent de5f075
Chris Beaven SmileyChris authored
39 django/core/paginator.py
View
@@ -1,4 +1,5 @@
from math import ceil
+import collections
class InvalidPage(Exception):
pass
@@ -84,6 +85,44 @@ def __init__(self, object_list, number, paginator):
def __repr__(self):
return '<Page %s of %s>' % (self.number, self.paginator.num_pages)
+ def __len__(self):
+ return len(self.object_list)
+
+ def __getitem__(self, index):
+ # The object_list is converted to a list so that if it was a QuerySet
+ # it won't be a database hit per __getitem__.
+ return list(self.object_list)[index]
+
+ # The following four methods are only necessary for Python <2.6
+ # compatibility (this class could just extend 2.6's collections.Sequence).
+
+ def __iter__(self):
+ i = 0
+ try:
+ while True:
+ v = self[i]
+ yield v
+ i += 1
+ except IndexError:
+ return
+
+ def __contains__(self, value):
+ for v in self:
+ if v == value:
+ return True
+ return False
+
+ def index(self, value):
+ for i, v in enumerate(self):
+ if v == value:
+ return i
+ raise ValueError
+
+ def count(self, value):
+ return sum([1 for v in self if v == value])
+
+ # End of compatibility methods.
+
def has_next(self):
return self.number < self.paginator.num_pages
27 docs/topics/pagination.txt
View
@@ -81,22 +81,20 @@ show how you can display the results. This example assumes you have a
The view function looks like this::
- from django.core.paginator import Paginator, InvalidPage, EmptyPage
+ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
def listing(request):
contact_list = Contacts.objects.all()
paginator = Paginator(contact_list, 25) # Show 25 contacts per page
- # Make sure page request is an int. If not, deliver first page.
- try:
- page = int(request.GET.get('page', '1'))
- except ValueError:
- page = 1
-
- # If page request (9999) is out of range, deliver last page of results.
+ page = request.GET.get('page')
try:
contacts = paginator.page(page)
- except (EmptyPage, InvalidPage):
+ 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)
return render_to_response('list.html', {"contacts": contacts})
@@ -104,7 +102,7 @@ The view function looks like this::
In the template :file:`list.html`, you'll want to include navigation between
pages along with any interesting information from the objects themselves::
- {% for contact in contacts.object_list %}
+ {% for contact in contacts %}
{# Each "contact" is a Contact model object. #}
{{ contact.full_name|upper }}<br />
...
@@ -126,6 +124,11 @@ pages along with any interesting information from the objects themselves::
</span>
</div>
+.. versionchanged:: 1.4
+ Previously, you would need to use
+ ``{% for contact in contacts.object_list %}``, since the ``Page``
+ object was not iterable.
+
``Paginator`` objects
=====================
@@ -194,6 +197,7 @@ Attributes
A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``.
+
``InvalidPage`` exceptions
==========================
@@ -221,6 +225,9 @@ them both with a simple ``except InvalidPage``.
You usually won't construct :class:`Pages <Page>` by hand -- you'll get them
using :meth:`Paginator.page`.
+.. versionadded:: 1.4
+ A page acts like a sequence of :attr:`Page.object_list` when using
+ ``len()`` or iterating it directly.
Methods
-------
12 tests/regressiontests/pagination_regress/tests.py
View
@@ -154,3 +154,15 @@ def test_page_indexes(self):
self.assertRaises(EmptyPage, self.check_indexes, ([], 4, 0, False), 1, None)
self.assertRaises(EmptyPage, self.check_indexes, ([], 4, 1, False), 1, None)
self.assertRaises(EmptyPage, self.check_indexes, ([], 4, 2, False), 1, None)
+
+ def test_page_sequence(self):
+ """
+ Tests that a paginator page acts like a standard sequence.
+ """
+ eleven = 'abcdefghijk'
+ page2 = Paginator(eleven, per_page=5, orphans=1).page(2)
+ self.assertEqual(len(page2), 6)
+ self.assertTrue('k' in page2)
+ self.assertFalse('a' in page2)
+ self.assertEqual(''.join(page2), 'fghijk')
+ self.assertEqual(''.join(reversed(page2)), 'kjihgf')
Please sign in to comment.
Something went wrong with that request. Please try again.