Fixed #19261 -- Delayed Queryset evaluation in paginators

Thanks trbs for the report and the patch.
This commit is contained in:
Claude Paroz 2012-11-09 19:37:50 +01:00
parent aea8bf0662
commit 1b307d6c8f
2 changed files with 27 additions and 0 deletions

View File

@ -1,6 +1,9 @@
import collections
from math import ceil
from django.utils import six
class InvalidPage(Exception):
pass
@ -89,6 +92,8 @@ class Page(collections.Sequence):
return len(self.object_list)
def __getitem__(self, index):
if not isinstance(index, (slice,) + six.integer_types):
raise TypeError
# 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]

View File

@ -266,3 +266,25 @@ class ModelPaginationTests(TestCase):
self.assertEqual(1, p.previous_page_number())
self.assertEqual(6, p.start_index())
self.assertEqual(9, p.end_index())
def test_page_getitem(self):
"""
Tests proper behaviour of a paginator page __getitem__ (queryset
evaluation, slicing, exception raised).
"""
paginator = Paginator(Article.objects.all(), 5)
p = paginator.page(1)
# Make sure object_list queryset is not evaluated by an invalid __getitem__ call.
# (this happens from the template engine when using eg: {% page_obj.has_previous %})
self.assertIsNone(p.object_list._result_cache)
self.assertRaises(TypeError, lambda: p['has_previous'])
self.assertIsNone(p.object_list._result_cache)
# Make sure slicing the Page object with numbers and slice objects work.
self.assertEqual(p[0], Article.objects.get(headline='Article 1'))
self.assertQuerysetEqual(p[slice(2)], [
"<Article: Article 1>",
"<Article: Article 2>",
]
)