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:
parent
de5f07513b
commit
4fa9646716
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
-------
|
-------
|
||||||
|
|
|
@ -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')
|
||||||
|
|
Loading…
Reference in New Issue