list.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. from urllib import urlencode
  2. from django.core.paginator import Paginator, EmptyPage
  3. from django.shortcuts import redirect
  4. from misago.core.exceptions import ExplicitFirstPage
  5. from misago.admin.views.generic.base import AdminView
  6. class ListView(AdminView):
  7. """
  8. Admin items list view
  9. Uses following attributes:
  10. template = template name used to render items list
  11. items_per_page = number of items displayed on single page
  12. (enter 0 or don't define for no pagination)
  13. ordering = tuple of tuples defining allowed orderings
  14. typles should follow this format: (name, order_by)
  15. """
  16. template = 'list.html'
  17. items_per_page = 0
  18. ordering = None
  19. extra_actions = None
  20. @classmethod
  21. def add_item_action(cls, name, icon, link, style=None):
  22. if not cls.extra_actions:
  23. cls.extra_actions = []
  24. cls.extra_actions.append({
  25. 'name': name,
  26. 'icon': icon,
  27. 'link': link,
  28. 'style': style,
  29. })
  30. def get_queryset(self):
  31. return self.get_model().objects.all()
  32. def paginate_items(self, context, page):
  33. try:
  34. page = int(page)
  35. if page == 1:
  36. raise ExplicitFirstPage()
  37. elif page == 0:
  38. page = 1
  39. except ValueError:
  40. page_no = 1
  41. context['paginator'] = Paginator(context['items'],
  42. self.items_per_page,
  43. allow_empty_first_page=True)
  44. context['page'] = context['paginator'].page(page)
  45. context['items'] = context['page'].object_list
  46. """
  47. Filter list items
  48. """
  49. @property
  50. def filters_token(self):
  51. return '%s:filters' % self.root_link
  52. def search_form(self, request, context):
  53. pass
  54. def filter_items(self, context):
  55. pass
  56. """
  57. Order list items
  58. """
  59. @property
  60. def ordering_session_key(self):
  61. return 'misago_admin_%s_order_by' % self.root_link
  62. def get_ordering_from_GET(self, request):
  63. sort = request.GET.get('sort')
  64. if request.GET.get('direction') == 'desc':
  65. new_ordering = '-%s' % sort
  66. elif request.GET.get('direction') == 'asc':
  67. new_ordering = sort
  68. else:
  69. new_ordering = '?nope'
  70. return self.clean_ordering(new_ordering)
  71. def get_ordering_from_session(self, request):
  72. new_ordering = request.session.get(self.ordering_session_key)
  73. return self.clean_ordering(new_ordering)
  74. def clean_ordering(self, new_ordering):
  75. for order_by, name in self.ordering:
  76. if order_by == new_ordering:
  77. return order_by
  78. else:
  79. return None
  80. def get_ordering_methods(self, request):
  81. return {
  82. 'GET': self.get_ordering_from_GET(request),
  83. 'session': self.get_ordering_from_session(request),
  84. 'default': self.clean_ordering(self.ordering[0][0]),
  85. }
  86. def get_ordering_method_to_use(self, methods):
  87. for method in ('GET', 'session', 'default'):
  88. if methods.get(method):
  89. return methods.get(method)
  90. def set_ordering_in_context(self, context, method):
  91. for order_by, name in self.ordering:
  92. order_as_dict = {
  93. 'type': 'desc' if order_by[0] == '-' else 'asc',
  94. 'order_by': order_by,
  95. 'name': name,
  96. }
  97. if order_by == method:
  98. context['order'] = order_as_dict
  99. context['items'] = context['items'].order_by(
  100. order_as_dict['order_by'])
  101. elif order_as_dict['name']:
  102. if order_as_dict['type'] == 'desc':
  103. order_as_dict['order_by'] = order_as_dict['order_by'][1:]
  104. context['order_by'].append(order_as_dict)
  105. """
  106. Querystrings builder
  107. """
  108. def make_querystrings(self, context):
  109. values = {}
  110. filter_values = {}
  111. order_values = {}
  112. if context['active_filters']:
  113. filter_values = context['active_filters']
  114. values.update(filter_values)
  115. if context['order_by']:
  116. order_values = {
  117. 'sort': context['order']['order_by'],
  118. 'direction': context['order']['type'],
  119. }
  120. if order_values['sort'][0] == '-':
  121. # We don't start sorting criteria with minus in querystring
  122. order_values['sort'] = order_values['sort'][1:]
  123. values.update(order_values)
  124. if values:
  125. context['querystring'] = '?%s' % urlencode(values)
  126. if order_values:
  127. context['querystring_order'] = '?%s' % urlencode(order_values)
  128. if filter_values:
  129. context['querystring_filter'] = '?%s' % urlencode(filter_values)
  130. """
  131. Dispatch response
  132. """
  133. def dispatch(self, request, *args, **kwargs):
  134. active_filters = request.session.get(self.filters_token, None)
  135. extra_actions_list = self.extra_actions or []
  136. refresh_querystring = False
  137. context = {
  138. 'items': self.get_queryset(),
  139. 'paginator': None,
  140. 'page': None,
  141. 'order_by': [],
  142. 'order': None,
  143. 'search_form': None,
  144. 'active_filters': active_filters,
  145. 'querystring': '',
  146. 'querystring_order': '',
  147. 'querystring_filter': '',
  148. 'extra_actions': extra_actions_list,
  149. 'extra_actions_len': len(extra_actions_list),
  150. }
  151. if self.ordering:
  152. ordering_methods = self.get_ordering_methods(request)
  153. used_method = self.get_ordering_method_to_use(ordering_methods)
  154. self.set_ordering_in_context(context, used_method)
  155. if (ordering_methods['GET'] and
  156. ordering_methods['GET'] != ordering_methods['session']):
  157. # Store get method in session for future requests
  158. session_key = self.ordering_session_key
  159. request.session[session_key] = ordering_methods['GET']
  160. if context['order_by'] and not ordering_methods['GET']:
  161. # Make view redirect to itself with querystring,
  162. # So address ball contains copy-friendly link
  163. refresh_querystring = True
  164. self.search_form(request, context)
  165. if active_filters:
  166. self.filter_items(context)
  167. if self.items_per_page:
  168. self.paginate_items(context, kwargs.get('page', 0))
  169. self.make_querystrings(context)
  170. if refresh_querystring:
  171. return redirect('%s%s' % (request.path, context['querystring']))
  172. return self.render(request, context)