diff --git a/django/core/paginator.py b/django/core/paginator.py
index 495cdf2..479cb49 100644
a
|
b
|
|
1 | 1 | from math import ceil |
| 2 | import collections |
2 | 3 | |
3 | 4 | class InvalidPage(Exception): |
4 | 5 | pass |
… |
… |
class Paginator(object):
|
75 | 76 | |
76 | 77 | QuerySetPaginator = Paginator # For backwards-compatibility. |
77 | 78 | |
78 | | class Page(object): |
| 79 | class Page(collections.Sequence): |
79 | 80 | def __init__(self, object_list, number, paginator): |
80 | 81 | self.object_list = object_list |
81 | 82 | self.number = number |
… |
… |
class Page(object):
|
84 | 85 | def __repr__(self): |
85 | 86 | return '<Page %s of %s>' % (self.number, self.paginator.num_pages) |
86 | 87 | |
| 88 | def __len__(self): |
| 89 | return len(self.object_list) |
| 90 | |
| 91 | def __getitem__(self, index): |
| 92 | # The object_list is converted to a list so that if it was a QuerySet |
| 93 | # it won't be a database hit per __getitem__. |
| 94 | return list(self.object_list)[index] |
| 95 | |
87 | 96 | def has_next(self): |
88 | 97 | return self.number < self.paginator.num_pages |
89 | 98 | |
diff --git a/docs/topics/pagination.txt b/docs/topics/pagination.txt
index db776aa..197b61b 100644
a
|
b
|
The view function looks like this::
|
104 | 104 | In the template :file:`list.html`, you'll want to include navigation between |
105 | 105 | pages along with any interesting information from the objects themselves:: |
106 | 106 | |
107 | | {% for contact in contacts.object_list %} |
| 107 | {% for contact in contacts %} |
108 | 108 | {# Each "contact" is a Contact model object. #} |
109 | 109 | {{ contact.full_name|upper }}<br /> |
110 | 110 | ... |
… |
… |
pages along with any interesting information from the objects themselves::
|
126 | 126 | </span> |
127 | 127 | </div> |
128 | 128 | |
| 129 | .. versionchanged:: 1.3 |
| 130 | Previously, you would need to use |
| 131 | ``{% for contact in contacts.object_list %}``, since the ``Page`` |
| 132 | object was not iterable. |
| 133 | |
129 | 134 | |
130 | 135 | ``Paginator`` objects |
131 | 136 | ===================== |
… |
… |
Attributes
|
194 | 199 | |
195 | 200 | A 1-based range of page numbers, e.g., ``[1, 2, 3, 4]``. |
196 | 201 | |
| 202 | |
197 | 203 | ``InvalidPage`` exceptions |
198 | 204 | ========================== |
199 | 205 | |
… |
… |
them both with a simple ``except InvalidPage``.
|
221 | 227 | You usually won't construct :class:`Pages <Page>` by hand -- you'll get them |
222 | 228 | using :meth:`Paginator.page`. |
223 | 229 | |
| 230 | .. versionadded:: 1.3 |
| 231 | A page acts like a sequence of :attr:`Page.object_list` when using |
| 232 | ``len()`` or iterating it directly. |
224 | 233 | |
225 | 234 | Methods |
226 | 235 | ------- |
diff --git a/tests/regressiontests/pagination_regress/tests.py b/tests/regressiontests/pagination_regress/tests.py
index 28fe316..1cddd43 100644
a
|
b
|
class PaginatorTests(TestCase):
|
154 | 154 | self.assertRaises(EmptyPage, self.check_indexes, ([], 4, 0, False), 1, None) |
155 | 155 | self.assertRaises(EmptyPage, self.check_indexes, ([], 4, 1, False), 1, None) |
156 | 156 | self.assertRaises(EmptyPage, self.check_indexes, ([], 4, 2, False), 1, None) |
| 157 | |
| 158 | def test_page_sequence(self): |
| 159 | """ |
| 160 | Tests that a paginator page acts like a standard sequence. |
| 161 | """ |
| 162 | eleven = 'abcdefghijk' |
| 163 | page2 = Paginator(eleven, per_page=5, orphans=1).page(2) |
| 164 | self.assertEqual(len(page2), 6) |
| 165 | self.assertTrue('k' in page2) |
| 166 | self.assertFalse('a' in page2) |
| 167 | self.assertEqual(''.join(page2), 'fghijk') |