views.py 15 KB

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