/django/views/generic/list.py

https://code.google.com/p/mango-py/ · Python · 155 lines · 99 code · 19 blank · 37 comment · 23 complexity · fa845944828173066276adb6e842d532 MD5 · raw file

  1. import re
  2. from django.core.paginator import Paginator, InvalidPage
  3. from django.core.exceptions import ImproperlyConfigured
  4. from django.http import Http404
  5. from django.utils.encoding import smart_str
  6. from django.utils.translation import ugettext as _
  7. from django.views.generic.base import TemplateResponseMixin, View
  8. class MultipleObjectMixin(object):
  9. allow_empty = True
  10. queryset = None
  11. model = None
  12. paginate_by = None
  13. context_object_name = None
  14. paginator_class = Paginator
  15. def get_queryset(self):
  16. """
  17. Get the list of items for this view. This must be an interable, and may
  18. be a queryset (in which qs-specific behavior will be enabled).
  19. """
  20. if self.queryset is not None:
  21. queryset = self.queryset
  22. if hasattr(queryset, '_clone'):
  23. queryset = queryset._clone()
  24. elif self.model is not None:
  25. queryset = self.model._default_manager.all()
  26. else:
  27. raise ImproperlyConfigured(u"'%s' must define 'queryset' or 'model'"
  28. % self.__class__.__name__)
  29. return queryset
  30. def paginate_queryset(self, queryset, page_size):
  31. """
  32. Paginate the queryset, if needed.
  33. """
  34. paginator = self.get_paginator(queryset, page_size, allow_empty_first_page=self.get_allow_empty())
  35. page = self.kwargs.get('page') or self.request.GET.get('page') or 1
  36. try:
  37. page_number = int(page)
  38. except ValueError:
  39. if page == 'last':
  40. page_number = paginator.num_pages
  41. else:
  42. raise Http404(_(u"Page is not 'last', nor can it be converted to an int."))
  43. try:
  44. page = paginator.page(page_number)
  45. return (paginator, page, page.object_list, page.has_other_pages())
  46. except InvalidPage:
  47. raise Http404(_(u'Invalid page (%(page_number)s)') % {
  48. 'page_number': page_number
  49. })
  50. def get_paginate_by(self, queryset):
  51. """
  52. Get the number of items to paginate by, or ``None`` for no pagination.
  53. """
  54. return self.paginate_by
  55. def get_paginator(self, queryset, per_page, orphans=0, allow_empty_first_page=True):
  56. """
  57. Return an instance of the paginator for this view.
  58. """
  59. return self.paginator_class(queryset, per_page, orphans=orphans, allow_empty_first_page=allow_empty_first_page)
  60. def get_allow_empty(self):
  61. """
  62. Returns ``True`` if the view should display empty lists, and ``False``
  63. if a 404 should be raised instead.
  64. """
  65. return self.allow_empty
  66. def get_context_object_name(self, object_list):
  67. """
  68. Get the name of the item to be used in the context.
  69. """
  70. if self.context_object_name:
  71. return self.context_object_name
  72. elif hasattr(object_list, 'model'):
  73. return smart_str('%s_list' % object_list.model._meta.object_name.lower())
  74. else:
  75. return None
  76. def get_context_data(self, **kwargs):
  77. """
  78. Get the context for this view.
  79. """
  80. queryset = kwargs.pop('object_list')
  81. page_size = self.get_paginate_by(queryset)
  82. context_object_name = self.get_context_object_name(queryset)
  83. if page_size:
  84. paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size)
  85. context = {
  86. 'paginator': paginator,
  87. 'page_obj': page,
  88. 'is_paginated': is_paginated,
  89. 'object_list': queryset
  90. }
  91. else:
  92. context = {
  93. 'paginator': None,
  94. 'page_obj': None,
  95. 'is_paginated': False,
  96. 'object_list': queryset
  97. }
  98. context.update(kwargs)
  99. if context_object_name is not None:
  100. context[context_object_name] = queryset
  101. return context
  102. class BaseListView(MultipleObjectMixin, View):
  103. def get(self, request, *args, **kwargs):
  104. self.object_list = self.get_queryset()
  105. allow_empty = self.get_allow_empty()
  106. if not allow_empty and len(self.object_list) == 0:
  107. raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
  108. % {'class_name': self.__class__.__name__})
  109. context = self.get_context_data(object_list=self.object_list)
  110. return self.render_to_response(context)
  111. class MultipleObjectTemplateResponseMixin(TemplateResponseMixin):
  112. template_name_suffix = '_list'
  113. def get_template_names(self):
  114. """
  115. Return a list of template names to be used for the request. Must return
  116. a list. May not be called if get_template is overridden.
  117. """
  118. try:
  119. names = super(MultipleObjectTemplateResponseMixin, self).get_template_names()
  120. except ImproperlyConfigured:
  121. # If template_name isn't specified, it's not a problem --
  122. # we just start with an empty list.
  123. names = []
  124. # If the list is a queryset, we'll invent a template name based on the
  125. # app and model name. This name gets put at the end of the template
  126. # name list so that user-supplied names override the automatically-
  127. # generated ones.
  128. if hasattr(self.object_list, 'model'):
  129. opts = self.object_list.model._meta
  130. names.append("%s/%s%s.html" % (opts.app_label, opts.object_name.lower(), self.template_name_suffix))
  131. return names
  132. class ListView(MultipleObjectTemplateResponseMixin, BaseListView):
  133. """
  134. Render some list of objects, set by `self.model` or `self.queryset`.
  135. `self.queryset` can actually be any iterable of items, not just a queryset.
  136. """