浏览代码

Fixes #11596 -- Make paginator.Page iterable

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16018 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Chris Beaven 14 年之前
父节点
当前提交
4fa9646716
共有 3 个文件被更改,包括 68 次插入10 次删除
  1. 39 0
      django/core/paginator.py
  2. 17 10
      docs/topics/pagination.txt
  3. 12 0
      tests/regressiontests/pagination_regress/tests.py

+ 39 - 0
django/core/paginator.py

@@ -1,4 +1,5 @@
 from math import ceil
+import collections
 
 class InvalidPage(Exception):
     pass
@@ -84,6 +85,44 @@ class Page(object):
     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
 

+ 17 - 10
docs/topics/pagination.txt

@@ -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 - 0
tests/regressiontests/pagination_regress/tests.py

@@ -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, 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')