views.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. # -*- coding: utf-8 -*-
  2. from django.shortcuts import get_object_or_404, redirect
  3. from django.views import generic
  4. from django.core.exceptions import PermissionDenied
  5. from django.template import RequestContext
  6. from django.utils.http import urlquote_plus
  7. from django.template import loader
  8. from django.http import HttpResponse
  9. from rapid.wrappers import InstanceData, ModelData
  10. from rapid.filters import FilterSet
  11. from rapidforms import for_model
  12. import math
  13. def _build_uri(request, params):
  14. param_string = "&".join(["%s=%s" % (urlquote_plus(k), urlquote_plus(params[k])) for k in params.keys()])
  15. base = request.build_absolute_uri().split("?")[0]
  16. return base + "?" + param_string
  17. def _copy_dict(dc):
  18. d = {}
  19. for k in dc.keys():
  20. d[k] = dc[k]
  21. return d
  22. def _replace_param(request, param_name, param_value):
  23. get = _copy_dict(request.GET)
  24. get[param_name] = param_value
  25. return _build_uri(request, get)
  26. def is_ajax_request(request):
  27. # X-Requested-With: XMLHttpRequest
  28. w = request.META.get('HTTP_X_REQUESTED_WITH')
  29. if w:
  30. return True
  31. return False
  32. class ListView(generic.list.ListView):
  33. template_name = 'rapid/bare/list.html'
  34. action_name = ''
  35. registers_per_page = 50
  36. number_of_edge_pages = 5
  37. number_of_middle_pages = 5
  38. fields = None
  39. class Pagination:
  40. def __init__(self, request, page, number_of_edge_pages, number_of_middle_pages,
  41. registers_per_page, total_pages):
  42. self.request = request
  43. self.number_of_edge_pages = number_of_edge_pages
  44. self.number_of_middle_pages = number_of_middle_pages
  45. self.registers_per_page = registers_per_page
  46. self.page = page
  47. self.total_pages = total_pages
  48. def _page_and_uri(self, pages):
  49. get2 = _copy_dict(self.request.GET)
  50. for p in pages:
  51. get2['page'] = p
  52. yield (p, _build_uri(self.request, get2))
  53. def start_numbers(self):
  54. """
  55. Page numbers listed at the beginning of the pagination area
  56. """
  57. e = self.number_of_edge_pages
  58. for i in range(1, min(e+1, self.page)):
  59. yield i
  60. def start(self):
  61. """
  62. Page numbers and URLs listed at the beginning of the pagination area
  63. """
  64. return self._page_and_uri(self.start_numbers())
  65. def before_numbers(self):
  66. """
  67. Page numbers listed at the pagination area before the current page
  68. """
  69. m = self.number_of_middle_pages
  70. for i in range(max(self.page - m, m), self.page):
  71. yield i
  72. def before(self):
  73. """
  74. Page numbers and URLs listed at the pagination area before the current page
  75. """
  76. return self._page_and_uri(self.before_numbers())
  77. def after_numbers(self):
  78. """
  79. Page numbers listed at the pagination area after the current page
  80. """
  81. m = self.number_of_middle_pages
  82. e = self.number_of_edge_pages
  83. for i in range(self.page + 1, min(self.page + m + 1, self.total_pages - e + 1)):
  84. yield i
  85. def after(self):
  86. """
  87. Page numbers and URLs listed at the pagination area after the current page
  88. """
  89. return self._page_and_uri(self.after_numbers())
  90. def end_numbers(self):
  91. """
  92. Page numbers listed at the end of the pagination area
  93. """
  94. e = self.number_of_edge_pages
  95. for i in range(max(self.page + 1, self.total_pages - e + 1), self.total_pages + 1):
  96. yield i
  97. def end(self):
  98. """
  99. Page numbers and URLs listed at the end of the pagination area
  100. """
  101. return self._page_and_uri(self.end_numbers())
  102. def separate_end(self):
  103. """
  104. True if there's a separator between the next pages and the end pages.
  105. """
  106. return self.page < self.total_pages - self.number_of_edge_pages - self.number_of_middle_pages
  107. def separate_start(self):
  108. """
  109. True if there's a separator between the start pages and the before pages.
  110. """
  111. return self.page > self.number_of_edge_pages + self.number_of_middle_pages
  112. def get_page(self, queryset):
  113. return queryset[self.registers_per_page*(self.page-1):self.registers_per_page*self.page]
  114. class View:
  115. order_param = 'order'
  116. def __init__(self, request, action_name, model, queryset, fields, pagination):
  117. self.request = request
  118. self.model = model
  119. self.action_name = action_name
  120. self._fields = fields
  121. q = queryset
  122. self.filters = FilterSet.from_request(ModelData(self.model), request)
  123. if self.filters:
  124. q = q.filter(**self.filters.query_dict())
  125. self.queryset = q
  126. total = q.count()
  127. pagination.total_pages = int(math.ceil(total / pagination.registers_per_page))
  128. order = request.GET.get(self.order_param)
  129. if order:
  130. q = q.order_by(order)
  131. self.queryset = pagination.get_page(q)
  132. def values(self):
  133. for o in self.queryset:
  134. if self._fields:
  135. c = InstanceData(o, request=self.request, fields=self._fields)
  136. else:
  137. c = InstanceData(o, request=self.request)
  138. if c.has_permission(self.request, self.action_name):
  139. yield c
  140. def fields(self):
  141. order_param = self.order_param
  142. request = self.request
  143. class FieldParams:
  144. def __init__(self, field):
  145. self.field = field
  146. def order_up_url(self):
  147. return _replace_param(request, order_param, self.field.bare_name())
  148. def order_down_url(self):
  149. return _replace_param(request, order_param, u'-' + self.field.bare_name())
  150. def add_filter_url(self):
  151. pass
  152. def del_filter_url(self):
  153. pass
  154. for f in ModelData(self.model, request=self.request, fields=self._fields).fields():
  155. f.view = FieldParams(f)
  156. yield f
  157. def get(self, request, **kwargs):
  158. mdata = ModelData(self.model, request)
  159. if not mdata.has_permission(request, self.action_name):
  160. raise PermissionDenied
  161. object_list = self.get_queryset()
  162. # Populates the context
  163. context = RequestContext(request).flatten()
  164. context.update(kwargs)
  165. total_pages = int(math.ceil(object_list.count() / self.registers_per_page))
  166. page = int(request.GET.get('page', 1))
  167. p = self.Pagination(request, page, self.number_of_edge_pages,
  168. self.number_of_middle_pages, self.registers_per_page,
  169. total_pages)
  170. context['pages'] = p
  171. context['model'] = ModelData(self.model, request, fields=self.fields)
  172. context['view'] = self.View(request, self.action_name, self.model, object_list, self.fields, p)
  173. offset = (page - 1) * self.registers_per_page
  174. self.object_list = object_list[offset: offset + self.registers_per_page]
  175. context[self.get_context_object_name(object_list)] = object_list
  176. context['object_list'] = object_list
  177. context = self.get_context_data(**context)
  178. # noinspection PyAttributeOutsideInit
  179. self.object_list = object_list
  180. return self.render_to_response(context)
  181. class ReadView(generic.detail.DetailView):
  182. template_name = 'rapid/bare/detail.html'
  183. action_name = ''
  184. request = None
  185. def get_object(self, request=None):
  186. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  187. # noinspection PyMethodOverriding
  188. def get(self, request, pk, **kwargs):
  189. context = RequestContext(request).flatten()
  190. context.update(kwargs)
  191. obj = self.get_object(self.request)
  192. context['object'] = obj
  193. context[self.get_context_object_name(obj)] = obj
  194. cd = InstanceData(obj, request=request)
  195. if not cd.has_permission(request, self.action_name):
  196. raise PermissionDenied
  197. excludes = request.GET.get('exclude')
  198. if excludes:
  199. cd.excludes = excludes.split(",")
  200. # noinspection PyAttributeOutsideInit
  201. self.object = cd
  202. context['object_data'] = cd
  203. context = self.get_context_data(**context)
  204. return self.render_to_response(context)
  205. class CreateView(generic.edit.CreateView):
  206. template_name = 'rapid/bare/create.html'
  207. action_name = ''
  208. object = None
  209. fields = '__all__'
  210. def request_form(self, request):
  211. return for_model(request, self.model)
  212. def get(self, request, **kwargs):
  213. context = RequestContext(request).flatten()
  214. context.update(kwargs)
  215. cd = ModelData(self.model, request=request)
  216. if not cd.has_permission(request, self.action_name):
  217. raise PermissionDenied
  218. context['model_data'] = cd
  219. if request.POST:
  220. context['form'] = self.request_form(request)(request.POST, request.FILES)
  221. else:
  222. context['form'] = self.request_form(request)()
  223. context = self.get_context_data(**context)
  224. return self.render_to_response(context)
  225. def post(self, request, **kwargs):
  226. m = ModelData(self.model, request=request)
  227. if m.has_permission(request, self.action_name):
  228. f = self.request_form(request)(request.POST, request.FILES)
  229. if f.is_valid():
  230. f.save()
  231. return redirect(m.list_url())
  232. return self.get(request, **kwargs)
  233. raise PermissionDenied()
  234. class UpdateView(generic.edit.UpdateView):
  235. template_name = 'rapid/bare/update.html'
  236. action_name = ''
  237. fields = '__all__'
  238. def get_object(self, queryset=None):
  239. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  240. def request_form(self, request):
  241. return for_model(request, self.model)
  242. # noinspection PyMethodOverriding
  243. def get(self, request, pk, **kwargs):
  244. context = RequestContext(request).flatten()
  245. context.update(kwargs)
  246. obj = self.get_object()
  247. context['object'] = obj
  248. context[self.get_context_object_name(obj)] = obj
  249. cd = InstanceData(obj, request=request)
  250. if not cd.has_permission(request, self.action_name):
  251. raise PermissionDenied
  252. # noinspection PyAttributeOutsideInit
  253. self.object = cd
  254. context['object_data'] = cd
  255. if request.POST:
  256. context['form'] = self.request_form(request)(request.POST, request.FILES, instance=obj)
  257. else:
  258. context['form'] = self.request_form(request)(instance=obj)
  259. context = self.get_context_data(**context)
  260. return self.render_to_response(context)
  261. # noinspection PyMethodOverriding,PyAttributeOutsideInit
  262. def post(self, request, pk, **kwargs):
  263. obj = self.get_object()
  264. m = InstanceData(obj, request=request)
  265. if m.has_permission(request, self.action_name):
  266. f = self.request_form(request)(request.POST, request.FILES, instance=obj)
  267. if f.is_valid():
  268. if f.instance.pk != obj.pk:
  269. raise PermissionDenied
  270. f.save()
  271. return redirect(m.list_url())
  272. self.form = f
  273. return self.get(request, pk, **kwargs)
  274. raise PermissionDenied
  275. class DeleteView(generic.edit.DeleteView):
  276. template_name = 'rapid/bare/delete.html'
  277. action_name = ''
  278. fields = '__all__'
  279. def __init__(self, **kwargs):
  280. super(DeleteView, self).__init__(**kwargs)
  281. self.success_url = ModelData(self.model).list_url()
  282. def get_object(self, queryset=None):
  283. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  284. def get(self, request, *args, **kwargs):
  285. obj = InstanceData(self.get_object())
  286. if not obj.has_permission(request, self.action_name):
  287. raise PermissionDenied
  288. return super(DeleteView, self).get(request, *args, **kwargs)
  289. def post(self, request, *args, **kwargs):
  290. obj = InstanceData(self.get_object())
  291. if not obj.has_permission(request, self.action_name):
  292. raise PermissionDenied
  293. return super(DeleteView, self).post(request, *args, **kwargs)
  294. class SelectView(ListView):
  295. """
  296. A selectable list of objects.
  297. """
  298. template_name = 'rapid/bare/select.html'
  299. def bare_or_main(view):
  300. main_window_template = "rapid/main_window.html"
  301. def vw(request, *args, **kwargs):
  302. resp = view(request, *args, **kwargs)
  303. bare = is_ajax_request(request)
  304. if bare:
  305. if 300 <= resp.status_code <= 399:
  306. # I'll interpret redirects as successful POST,
  307. # thus, the response'll get replaced by something with
  308. # a header that says "success!"
  309. ret = HttpResponse('')
  310. ret['X-FORM-STATUS:'] = 'success'
  311. return ret
  312. if not bare and resp.status_code == 200 and request.method != "HEAD":
  313. resp.render()
  314. body = resp.content
  315. main_win = loader.get_template(main_window_template)
  316. context = RequestContext(request)
  317. context.update({'body_text': body, 'this_url': request.get_full_path()})
  318. resp.content = main_win.render(context, request)
  319. return resp
  320. return vw
  321. def update_form_class(form):
  322. class F(UpdateView):
  323. def request_form(self, request):
  324. return form
  325. return F