mirror of
https://github.com/django/django.git
synced 2025-02-09 08:55:32 +00:00
Fixed #17535 -- Optimized list generic views.
When allow_empty is False, prevented the view from loading the entire queryset in memory when pagination is enabled.
This commit is contained in:
parent
006c2b8fc1
commit
009e237cf0
@ -273,7 +273,7 @@ class BaseDateListView(MultipleObjectMixin, DateMixin, View):
|
|||||||
if not allow_empty:
|
if not allow_empty:
|
||||||
# When pagination is enabled, it's better to do a cheap query
|
# When pagination is enabled, it's better to do a cheap query
|
||||||
# than to load the unpaginated queryset in memory.
|
# than to load the unpaginated queryset in memory.
|
||||||
is_empty = not bool(qs) if paginate_by is None else not qs.exists()
|
is_empty = len(qs) == 0 if paginate_by is None else not qs.exists()
|
||||||
if is_empty:
|
if is_empty:
|
||||||
raise Http404(_(u"No %(verbose_name_plural)s available") % {
|
raise Http404(_(u"No %(verbose_name_plural)s available") % {
|
||||||
'verbose_name_plural': force_unicode(qs.model._meta.verbose_name_plural)
|
'verbose_name_plural': force_unicode(qs.model._meta.verbose_name_plural)
|
||||||
|
@ -16,7 +16,7 @@ class MultipleObjectMixin(ContextMixin):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
Get the list of items for this view. This must be an interable, and may
|
Get the list of items for this view. This must be an iterable, and may
|
||||||
be a queryset (in which qs-specific behavior will be enabled).
|
be a queryset (in which qs-specific behavior will be enabled).
|
||||||
"""
|
"""
|
||||||
if self.queryset is not None:
|
if self.queryset is not None:
|
||||||
@ -113,9 +113,19 @@ class BaseListView(MultipleObjectMixin, View):
|
|||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
self.object_list = self.get_queryset()
|
self.object_list = self.get_queryset()
|
||||||
allow_empty = self.get_allow_empty()
|
allow_empty = self.get_allow_empty()
|
||||||
if not allow_empty and len(self.object_list) == 0:
|
|
||||||
raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
|
if not allow_empty:
|
||||||
% {'class_name': self.__class__.__name__})
|
# When pagination is enabled and object_list is a queryset,
|
||||||
|
# it's better to do a cheap query than to load the unpaginated
|
||||||
|
# queryset in memory.
|
||||||
|
if (self.get_paginate_by(self.object_list) is not None
|
||||||
|
and hasattr(self.object_list, 'exists')):
|
||||||
|
is_empty = not self.object_list.exists()
|
||||||
|
else:
|
||||||
|
is_empty = len(self.object_list) == 0
|
||||||
|
if is_empty:
|
||||||
|
raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
|
||||||
|
% {'class_name': self.__class__.__name__})
|
||||||
context = self.get_context_data(object_list=self.object_list)
|
context = self.get_context_data(object_list=self.object_list)
|
||||||
return self.render_to_response(context)
|
return self.render_to_response(context)
|
||||||
|
|
||||||
|
@ -159,6 +159,16 @@ class ListViewTests(TestCase):
|
|||||||
def test_missing_items(self):
|
def test_missing_items(self):
|
||||||
self.assertRaises(ImproperlyConfigured, self.client.get, '/list/authors/invalid/')
|
self.assertRaises(ImproperlyConfigured, self.client.get, '/list/authors/invalid/')
|
||||||
|
|
||||||
|
def test_paginated_list_view_does_not_load_entire_table(self):
|
||||||
|
# Regression test for #17535
|
||||||
|
self._make_authors(3)
|
||||||
|
# 1 query for authors
|
||||||
|
with self.assertNumQueries(1):
|
||||||
|
self.client.get('/list/authors/notempty/')
|
||||||
|
# same as above + 1 query to test if authors exist + 1 query for pagination
|
||||||
|
with self.assertNumQueries(3):
|
||||||
|
self.client.get('/list/authors/notempty/paginated/')
|
||||||
|
|
||||||
def _make_authors(self, n):
|
def _make_authors(self, n):
|
||||||
Author.objects.all().delete()
|
Author.objects.all().delete()
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
|
@ -128,6 +128,8 @@ urlpatterns = patterns('',
|
|||||||
views.AuthorList.as_view(paginate_by=30)),
|
views.AuthorList.as_view(paginate_by=30)),
|
||||||
(r'^list/authors/notempty/$',
|
(r'^list/authors/notempty/$',
|
||||||
views.AuthorList.as_view(allow_empty=False)),
|
views.AuthorList.as_view(allow_empty=False)),
|
||||||
|
(r'^list/authors/notempty/paginated/$',
|
||||||
|
views.AuthorList.as_view(allow_empty=False, paginate_by=2)),
|
||||||
(r'^list/authors/template_name/$',
|
(r'^list/authors/template_name/$',
|
||||||
views.AuthorList.as_view(template_name='generic_views/list.html')),
|
views.AuthorList.as_view(template_name='generic_views/list.html')),
|
||||||
(r'^list/authors/template_name_suffix/$',
|
(r'^list/authors/template_name_suffix/$',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user