From 9a5301ccbc08d5e4f78ae6a8a39330196cdd5f66 Mon Sep 17 00:00:00 2001 From: Gary Wilson Jr Date: Sun, 27 Jul 2008 23:01:55 +0000 Subject: [PATCH] Made the Paginator class a bit more backwards compatible with the lecacy `ObjectPaginator` class by using the `ObjectPaginator`'s `_get_count` method. Instead of explicitly checking for an instance of `QuerySet`, this now allows any object with a `count()` or `__len__()` method defined to be passed to Paginator. For one, this is useful when you have custom `QuerySet`-like classes that implement a `count()` method but don't inherit from `QuerySet` explicitly. git-svn-id: http://code.djangoproject.com/svn/django/trunk@8121 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/paginator.py | 24 ++++++------------------ docs/pagination.txt | 10 ++++++---- tests/modeltests/pagination/models.py | 25 +++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/django/core/paginator.py b/django/core/paginator.py index 439b0bc717..366d2986ab 100644 --- a/django/core/paginator.py +++ b/django/core/paginator.py @@ -42,10 +42,12 @@ class Paginator(object): def _get_count(self): "Returns the total number of objects, across all pages." if self._count is None: - from django.db.models.query import QuerySet - if isinstance(self.object_list, QuerySet): + try: self._count = self.object_list.count() - else: + except (AttributeError, TypeError): + # AttributeError if object_list has no count() method. + # TypeError if object_list.count() requires arguments + # (i.e. is of type list). self._count = len(self.object_list) return self._count count = property(_get_count) @@ -169,22 +171,8 @@ class ObjectPaginator(Paginator): return self.count return page_number * self.num_per_page - def _get_count(self): - # The old API allowed for self.object_list to be either a QuerySet or a - # list. Here, we handle both. - if self._count is None: - try: - self._count = self.object_list.count() - except (AttributeError, TypeError): - # AttributeError if object_list has no count() method. - # TypeError if object_list.count() requires arguments - # (i.e. is of type list). - self._count = len(self.object_list) - return self._count - count = property(_get_count) - # The old API called it "hits" instead of "count". - hits = count + hits = Paginator.count # The old API called it "pages" instead of "num_pages". pages = Paginator.num_pages diff --git a/docs/pagination.txt b/docs/pagination.txt index bfdf8eea1c..28b381ac12 100644 --- a/docs/pagination.txt +++ b/docs/pagination.txt @@ -59,10 +59,12 @@ page:: ... InvalidPage -Note that you can give ``Paginator`` a list/tuple or a Django ``QuerySet``. The -only difference is in implementation; if you pass a ``QuerySet``, the -``Paginator`` will call its ``count()`` method instead of using ``len()``, -because the former is more efficient. +Note that you can give ``Paginator`` a list/tuple, a Django ``QuerySet``, or +any other object with a ``count()`` or ``__len__()`` method. When determining +the number of objects contained in the passed object, ``Paginator`` will first +try calling ``count()``, then fallback to using ``len()`` if the passed object +has no ``count()`` method. This allows objects such as Django's ``QuerySet`` to +use a more efficient ``count()`` method when available. ``Paginator`` objects ===================== diff --git a/tests/modeltests/pagination/models.py b/tests/modeltests/pagination/models.py index 834c8e5ebd..e8b373e158 100644 --- a/tests/modeltests/pagination/models.py +++ b/tests/modeltests/pagination/models.py @@ -140,6 +140,31 @@ True >>> p.end_index() 5 +# Paginator can be passed other objects with a count() method. +>>> class CountContainer: +... def count(self): +... return 42 +>>> paginator = Paginator(CountContainer(), 10) +>>> paginator.count +42 +>>> paginator.num_pages +5 +>>> paginator.page_range +[1, 2, 3, 4, 5] + +# Paginator can be passed other objects that implement __len__. +>>> class LenContainer: +... def __len__(self): +... return 42 +>>> paginator = Paginator(LenContainer(), 10) +>>> paginator.count +42 +>>> paginator.num_pages +5 +>>> paginator.page_range +[1, 2, 3, 4, 5] + + ################################ # Legacy API (ObjectPaginator) # ################################