generic.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. from django.contrib import messages
  2. from django.core.paginator import Paginator, EmptyPage
  3. from django.shortcuts import redirect
  4. from django.utils.translation import ugettext_lazy as _
  5. from django.views.generic import View
  6. from misago.core.exceptions import ExplicitFirstPage
  7. from misago.admin import site
  8. from misago.admin.views import render
  9. class AdminBaseMixin(object):
  10. """
  11. Admin mixin abstraciton used for configuring admin CRUD views.
  12. Takes following attributes:
  13. Model = Model instance
  14. message_404 = string used in "requested item not found" messages
  15. root_link = name of link leading to root action (eg. list of all items
  16. template_dir = directory with templates
  17. """
  18. Model = None
  19. message_404 = None
  20. root_link = None
  21. template_dir = None
  22. def get_model(self):
  23. return self.Model
  24. class AdminView(View):
  25. def final_template(self):
  26. return '%s/%s' % (self.template_dir, self.template)
  27. def get_target(self, target):
  28. Model = self.get_model()
  29. return Model.objects.get(id=target)
  30. def _get_target(self, request, kwargs):
  31. """
  32. get_target is called by view to fetch item from DB
  33. """
  34. Model = self.get_model()
  35. try:
  36. return self.get_target(target)
  37. except Model.DoesNotExist:
  38. messages.error(request, self.message_404)
  39. return redirect(self.root_link)
  40. def current_link(self, request):
  41. matched_url = request.resolver_match.url_name
  42. return '%s:%s' % (request.resolver_match.namespace, matched_url)
  43. def process_context(self, request, context):
  44. return context
  45. def render(self, request, context=None):
  46. context = context or {}
  47. context['root_link'] = self.root_link
  48. context['current_link'] = self.current_link(request)
  49. self.process_context(request, context)
  50. return render(request, self.final_template(), context)
  51. class ListView(AdminView):
  52. """
  53. Admin items list view
  54. Uses following attributes:
  55. template = template name used to render items list
  56. items_per_page = number of items displayed on single page
  57. (enter 0 or don't define for no pagination)
  58. ordering = tuple of tuples defining allowed orderings
  59. typles should follow this format: (name, order_by)
  60. """
  61. template = 'list.html'
  62. items_per_page = 0
  63. ordering = None
  64. def get_queryset(self):
  65. return self.get_model().objects.all()
  66. def paginate_items(self, context, page):
  67. try:
  68. page = int(page)
  69. if page == 1:
  70. raise ExplicitFirstPage()
  71. elif page == 0:
  72. page = 1
  73. except ValueError:
  74. page_no = 1
  75. context['paginator'] = Paginator(context['items'],
  76. self.items_per_page,
  77. allow_empty_first_page=True)
  78. context['page'] = context['paginator'].page(page)
  79. context['items'] = context['page'].object_list
  80. def set_filters(self, request):
  81. pass
  82. def filter_items(self, request, context):
  83. context['is_filtering'] = False
  84. def ordering_session_key(self):
  85. return 'misago_admin_%s_order_by' % self.root_link
  86. def set_ordering(self, request, new_order):
  87. for order in self.ordering:
  88. if order[1] == new_order:
  89. request.session[self.ordering_session_key()] = order[1]
  90. return redirect(self.current_link(request))
  91. else:
  92. messages.error(request, _("New sorting method is incorrect."))
  93. raise ValueError()
  94. def order_items(self, request, context):
  95. current_ordering = request.session.get(self.ordering_session_key())
  96. for order in self.ordering:
  97. order_as_dict = {
  98. 'name': order[0],
  99. 'order_by': order[1],
  100. 'type': 'desc' if order[1][0] == '-' else 'asc',
  101. }
  102. if order[1] == current_ordering:
  103. context['order'] = order_as_dict
  104. context['items'] = context['items'].order_by(
  105. order_as_dict['order_by'])
  106. else:
  107. context['order_by'].append(order_as_dict)
  108. if not context['order']:
  109. current_ordering = context['order_by'].pop(0)
  110. context['order'] = current_ordering
  111. context['items'] = context['items'].order_by(
  112. current_ordering['order_by'])
  113. def dispatch(self, request, *args, **kwargs):
  114. context = {
  115. 'items': self.get_queryset(),
  116. 'paginator': None,
  117. 'page': None,
  118. 'order_by': [],
  119. 'order': None,
  120. }
  121. if self.ordering:
  122. if request.method == 'POST' and 'order_by' in request.POST:
  123. try:
  124. return self.set_ordering(request,
  125. request.POST.get('order_by'))
  126. except ValueError:
  127. pass
  128. self.order_items(request, context)
  129. if self.items_per_page:
  130. self.paginate_items(context, kwargs.get('page', 0))
  131. return self.render(request, context)
  132. class TargetedView(AdminView):
  133. def check_permissions(self, request, target=None):
  134. pass
  135. def get_target(self, kwargs):
  136. if len(kwargs):
  137. return self.get_model().objects.get(pk=kwargs[kwargs.keys()[0]])
  138. else:
  139. return self.get_model()()
  140. def get_target_or_none(self, request, kwargs):
  141. try:
  142. return self.get_target(kwargs)
  143. except self.get_model().DoesNotExist:
  144. return None
  145. def dispatch(self, request, *args, **kwargs):
  146. target = self.get_target_or_none(request, kwargs)
  147. if not target:
  148. messages.error(request, self.message_404)
  149. return redirect(self.root_link)
  150. error = self.check_permissions(request, target)
  151. if error:
  152. messages.error(request, error)
  153. return redirect(self.root_link)
  154. return self.real_dispatch(request, target)
  155. def real_dispatch(self, request, target=None):
  156. pass
  157. class FormView(TargetedView):
  158. form = None
  159. template = 'form.html'
  160. message_submit = None
  161. def create_form(self, request, target=None):
  162. return self.form
  163. def initialize_form(self, FormType, request, target=None):
  164. if request.method == 'POST':
  165. return self.form(request.POST, request.FILES, instance=target)
  166. else:
  167. return self.form(instance=target)
  168. def handle_form(self, form, request):
  169. form.instance.save()
  170. if self.message_submit:
  171. message = self.message_submit % unicode(form.instance)
  172. messages.success(request, message)
  173. def real_dispatch(self, request, target=None):
  174. FormType = self.create_form(request, target)
  175. form = self.initialize_form(FormType, request, target)
  176. if form.is_valid():
  177. self.handle_form(form, request)
  178. if 'stay' in request.POST:
  179. return redirect(request.path)
  180. else:
  181. return redirect(self.root_link)
  182. return self.render(request, {'form': form, 'target': target})
  183. class ButtonView(TargetedView):
  184. def real_dispatch(self, request, target=None):
  185. if request.method == 'POST':
  186. new_response = self.button_action(request, target)
  187. if new_response:
  188. return new_response
  189. return redirect(self.root_link)
  190. def button_action(self, request, target=None):
  191. raise NotImplementedError("You have to define custom button_action.")