Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[1.5.x] Fixed #19261 -- Delayed Queryset evaluation in paginators

Thanks trbs for the report and the patch.
Backport of 1b307d6 from master.
  • Loading branch information...
commit dc95791e61750024a610b6c5cf4d32b7325fcb51 1 parent bf35fb6
@claudep claudep authored
View
5 django/core/paginator.py
@@ -1,5 +1,8 @@
from math import ceil
+from django.utils import six
+
+
class InvalidPage(Exception):
pass
@@ -88,6 +91,8 @@ def __len__(self):
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
22 tests/regressiontests/pagination/tests.py
@@ -266,3 +266,25 @@ def test_last_page(self):
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>",
+ ]
+ )
Please sign in to comment.
Something went wrong with that request. Please try again.