Fixes #11596 -- Make paginator.Page iterable

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16018 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Chris Beaven 2011-04-06 09:40:26 +00:00
parent de5f07513b
commit 4fa9646716
3 changed files with 68 additions and 10 deletions

View File

@ -1,4 +1,5 @@
from math import ceil from math import ceil
import collections
class InvalidPage(Exception): class InvalidPage(Exception):
pass pass
@ -84,6 +85,44 @@ class Page(object):
def __repr__(self): def __repr__(self):
return '<Page %s of %s>' % (self.number, self.paginator.num_pages) 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): def has_next(self):
return self.number < self.paginator.num_pages return self.number < self.paginator.num_pages

View File

@ -81,22 +81,20 @@ show how you can display the results. This example assumes you have a
The view function looks like this:: 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): def listing(request):
contact_list = Contacts.objects.all() contact_list = Contacts.objects.all()
paginator = Paginator(contact_list, 25) # Show 25 contacts per page paginator = Paginator(contact_list, 25) # Show 25 contacts per page
# Make sure page request is an int. If not, deliver first page. page = request.GET.get('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.
try: try:
contacts = paginator.page(page) 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) contacts = paginator.page(paginator.num_pages)
return render_to_response('list.html', {"contacts": contacts}) 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 In the template :file:`list.html`, you'll want to include navigation between
pages along with any interesting information from the objects themselves:: 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. #} {# Each "contact" is a Contact model object. #}
{{ contact.full_name|upper }}<br /> {{ contact.full_name|upper }}<br />
... ...
@ -126,6 +124,11 @@ pages along with any interesting information from the objects themselves::
</span> </span>
</div> </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 ``Paginator`` objects
===================== =====================
@ -194,6 +197,7 @@ Attributes
A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``. A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``.
``InvalidPage`` exceptions ``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 You usually won't construct :class:`Pages <Page>` by hand -- you'll get them
using :meth:`Paginator.page`. 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 Methods
------- -------

View File

@ -154,3 +154,15 @@ class PaginatorTests(TestCase):
self.assertRaises(EmptyPage, self.check_indexes, ([], 4, 0, False), 1, None) 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, 1, False), 1, None)
self.assertRaises(EmptyPage, self.check_indexes, ([], 4, 2, 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')