1
0
mirror of https://github.com/django/django.git synced 2025-05-04 14:14:37 +00:00
Russell Keith-Magee 0fcb094557 Fixed #6735 -- Added class-based views.
This patch is the result of the work of many people, over many years.
To try and thank individuals would inevitably lead to many people
being left out or forgotten -- so rather than try to give a list that
will inevitably be incomplete, I'd like to thank *everybody* who
contributed in any way, big or small, with coding, testing, feedback
and/or documentation over the multi-year process of getting this into
trunk.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14254 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2010-10-18 13:34:47 +00:00

139 lines
5.2 KiB
Python

from django.core.paginator import Paginator, InvalidPage
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404
from django.utils.encoding import smart_str
from django.views.generic.base import TemplateResponseMixin, View
class MultipleObjectMixin(object):
allow_empty = True
queryset = None
model = None
paginate_by = None
context_object_name = None
def get_queryset(self):
"""
Get the list of items for this view. This must be an interable, and may
be a queryset (in which qs-specific behavior will be enabled).
"""
if self.queryset is not None:
queryset = self.queryset
if hasattr(queryset, '_clone'):
queryset = queryset._clone()
elif self.model is not None:
queryset = self.model._default_manager.all()
else:
raise ImproperlyConfigured(u"'%s' must define 'queryset' or 'model'"
% self.__class__.__name__)
return queryset
def paginate_queryset(self, queryset, page_size):
"""
Paginate the queryset, if needed.
"""
if queryset.count() > page_size:
paginator = Paginator(queryset, page_size, allow_empty_first_page=self.get_allow_empty())
page = self.kwargs.get('page', None) or self.request.GET.get('page', 1)
try:
page_number = int(page)
except ValueError:
if page == 'last':
page_number = paginator.num_pages
else:
raise Http404("Page is not 'last', nor can it be converted to an int.")
try:
page = paginator.page(page_number)
return (paginator, page, page.object_list, True)
except InvalidPage:
raise Http404(u'Invalid page (%s)' % page_number)
else:
return (None, None, queryset, False)
def get_paginate_by(self, queryset):
"""
Get the number of items to paginate by, or ``None`` for no pagination.
"""
return self.paginate_by
def get_allow_empty(self):
"""
Returns ``True`` if the view should display empty lists, and ``False``
if a 404 should be raised instead.
"""
return self.allow_empty
def get_context_object_name(self, object_list):
"""
Get the name of the item to be used in the context.
"""
if self.context_object_name:
return self.context_object_name
elif hasattr(object_list, 'model'):
return smart_str(object_list.model._meta.verbose_name_plural)
else:
return None
def get_context_data(self, **kwargs):
"""
Get the context for this view.
"""
queryset = kwargs.get('object_list')
page_size = self.get_paginate_by(queryset)
if page_size:
paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size)
context = {
'paginator': paginator,
'page_obj': page,
'is_paginated': is_paginated,
'object_list': queryset
}
else:
context = {
'paginator': None,
'page_obj': None,
'is_paginated': False,
'object_list': queryset
}
context.update(kwargs)
context_object_name = self.get_context_object_name(queryset)
if context_object_name is not None:
context[context_object_name] = queryset
return context
class BaseListView(MultipleObjectMixin, View):
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
if not allow_empty and len(self.object_list) == 0:
raise Http404(u"Empty list and '%s.allow_empty' is False."
% self.__class__.__name__)
context = self.get_context_data(object_list=self.object_list)
return self.render_to_response(context)
class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
template_name_suffix = '_list'
def get_template_names(self):
"""
Return a list of template names to be used for the request. Must return
a list. May not be called if get_template is overridden.
"""
names = super(MultipleObjectTemplateResponseMixin, self).get_template_names()
# If the list is a queryset, we'll invent a template name based on the
# app and model name. This name gets put at the end of the template
# name list so that user-supplied names override the automatically-
# generated ones.
if hasattr(self.object_list, 'model'):
opts = self.object_list.model._meta
names.append("%s/%s%s.html" % (opts.app_label, opts.object_name.lower(), self.template_name_suffix))
return names
class ListView(MultipleObjectTemplateResponseMixin, BaseListView):
"""
Render some list of objects, set by `self.model` or `self.queryset`.
`self.queryset` can actually be any iterable of items, not just a queryset.
"""