Commit 1b307d6c authored by Claude Paroz's avatar Claude Paroz
Browse files

Fixed #19261 -- Delayed Queryset evaluation in paginators

Thanks trbs for the report and the patch.
parent aea8bf06
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
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]
+22 −0
Original line number Diff line number Diff line
@@ -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>",
            ]
        )