views.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  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.registry import registry, Action, MenuEntry
  11. from rapid.wrappers import InstanceData, ModelData, FieldData
  12. from rapid import permissions
  13. from rapid.widgets import RapidSelector, RapidRelationReadOnly
  14. from rapid.filters import FilterSet
  15. import math
  16. def _build_uri(request, params):
  17. param_string = "&".join(["%s=%s"%(urlquote_plus(k), urlquote_plus(params[k])) for k in params.keys()])
  18. base = request.build_absolute_uri().split("?")[0]
  19. return base + "?" + param_string
  20. def _copy_dict(dc):
  21. d = {}
  22. for k in dc.keys():
  23. d[k] = dc[k]
  24. return d
  25. def _replace_param(request, param_name, param_value):
  26. get = _copy_dict(request.GET)
  27. get[param_name] = param_value
  28. return _build_uri(request, get)
  29. def is_ajax_request(request):
  30. # X-Requested-With: XMLHttpRequest
  31. w = request.META.get('HTTP_X_REQUESTED_WITH')
  32. if w:
  33. return True
  34. return False
  35. class ListView(generic.list.ListView):
  36. template_name = 'rapid/bare/list.html'
  37. action_name = ''
  38. registers_per_page = 50
  39. number_of_edge_pages = 3
  40. number_of_middle_pages = 3
  41. fields = None
  42. class Pagination:
  43. def __init__(self, request, page, number_of_edge_pages, number_of_middle_pages, registers_per_page, total_pages):
  44. self.request = request
  45. self.number_of_edge_pages = number_of_edge_pages
  46. self.number_of_middle_pages = number_of_middle_pages
  47. self.registers_per_page = registers_per_page
  48. self.page = page
  49. self.total_pages = total_pages
  50. def _page_and_uri(self, pages):
  51. get2 = _copy_dict(self.request.GET)
  52. for p in pages:
  53. get2['page'] = p
  54. yield (p, _build_uri(self.request, get2))
  55. def start_numbers(self):
  56. """
  57. Números das páginas que serão listadas no começo da paginação.
  58. """
  59. e = self.number_of_edge_pages
  60. for i in range(1, min(e, self.page - 1)):
  61. yield i
  62. def start(self):
  63. """
  64. Números e URL páginas que serão listadas no começo da paginação.
  65. """
  66. return self._page_and_uri(self.start_numbers())
  67. def before_numbers(self):
  68. """
  69. Números das páginas que serão listadas na paginação antes da atual.
  70. """
  71. m = self.number_of_middle_pages
  72. for i in range(max(self.page - m, m), self.page - 1):
  73. yield i
  74. def before(self):
  75. """
  76. Números e URL das páginas que serão listadas na paginação antes da atual.
  77. """
  78. return self._page_and_uri(self.before_numbers())
  79. def after_numbers(self):
  80. """
  81. Números das páginas que serão listadas na paginação depois da atual.
  82. """
  83. m = self.number_of_middle_pages
  84. e = self.number_of_edge_pages
  85. for i in range(self.page + 1, min(self.page + m, self.total_pages - e)):
  86. yield i
  87. def after(self):
  88. """
  89. Números e URL das páginas que serão listadas na paginação depois da atual.
  90. """
  91. return self._page_and_uri(self.after_numbers())
  92. def end_numbers(self):
  93. """
  94. Números das páginas que serão listadas no final da paginação.
  95. """
  96. e = self.number_of_edge_pages
  97. for i in range(max(self.page + 1, self.total_pages - e), self.total_pages):
  98. yield i
  99. def end(self):
  100. """
  101. Números e URLs das páginas que serão listadas no final da paginação.
  102. """
  103. return self._page_and_uri(self.end_numbers())
  104. def separate_end(self):
  105. """
  106. Indica se deve haver um separador entre o começo da paginação e as páginas antes da atual.
  107. """
  108. return self.page < self.total_pages - self.number_of_edge_pages - self.number_of_middle_pages
  109. def separate_start(self):
  110. """
  111. Indica se deve haver um separador entre as páginas depois da atual e o fim da paginação.
  112. """
  113. return self.page > self.number_of_edge_pages + self.number_of_middle_pages
  114. class View:
  115. order_param = 'order'
  116. def __init__(self, request, action_name, model, queryset, fields):
  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. order = request.GET.get(self.order_param)
  127. if order:
  128. q = q.order_by(order)
  129. self.queryset = q
  130. def values(self):
  131. for o in self.queryset:
  132. if self._fields:
  133. c = InstanceData(o, request=self.request, fields=self._fields)
  134. else:
  135. c = InstanceData(o, request=self.request)
  136. if c.has_permission(self.request, self.action_name):
  137. yield c
  138. def fields(self):
  139. order_param = self.order_param
  140. request = self.request
  141. class FieldParams:
  142. def __init__(self, field):
  143. self.field = field
  144. def order_up_url(self):
  145. return _replace_param(request, order_param, self.field.bare_name())
  146. def order_down_url(self):
  147. return _replace_param(request, order_param, u'-' + self.field.bare_name())
  148. def add_filter_url(self):
  149. pass
  150. def del_filter_url(self):
  151. pass
  152. for f in ModelData(self.model, request=self.request, fields=self._fields).fields():
  153. f.view = FieldParams(f)
  154. yield f
  155. def get(self, request, **kwargs):
  156. mdata = ModelData(self.model, request)
  157. if not mdata.has_permission(request, self.action_name):
  158. raise PermissionDenied
  159. #De forma similar ao get de ListView, recupera os objetos e verifica
  160. #se pode mostrar uma lista vazia.
  161. object_list = self.get_queryset()
  162. allow_empty = self.get_allow_empty()
  163. # Agora, popula o contexto com os request processors e os dados específicos
  164. # da listagem nos SA.
  165. context = RequestContext(request).flatten()
  166. context.update(kwargs)
  167. total_pages = int(math.ceil(object_list.count() / self.registers_per_page)) + 1
  168. page = int(request.GET.get('page', 1))
  169. p = self.Pagination(request, page, self.number_of_edge_pages,
  170. self.number_of_middle_pages, self.registers_per_page,
  171. total_pages)
  172. context['pages'] = p
  173. context['model'] = ModelData(self.model, request, fields=self.fields)
  174. context['view'] = self.View(request, self.action_name, self.model, object_list, self.fields)
  175. default_ordering = self.model._meta.ordering if hasattr(self.model._meta, 'ordering') else ''
  176. ordering = request.GET.get('order', default_ordering)
  177. offset = (page - 1) * self.registers_per_page
  178. self.object_list = object_list[offset: offset + self.registers_per_page]
  179. if len(self.object_list) > 0:
  180. context['has_objects'] = True
  181. context['first_object'] = self.object_list[0]
  182. else:
  183. context['has_objects'] = False
  184. context[self.get_context_object_name(object_list)] = object_list
  185. context['object_list'] = object_list
  186. context = self.get_context_data(**context)
  187. self.object_list = object_list
  188. return self.render_to_response(context)
  189. class ReadView(generic.detail.DetailView):
  190. template_name = 'rapid/bare/detail.html'
  191. action_name = ''
  192. request = None
  193. def get_object(self, request=None):
  194. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  195. def get(self, request, pk, **kwargs):
  196. context = RequestContext(request).flatten()
  197. context.update(kwargs)
  198. obj = self.get_object(self.request)
  199. context['object'] = obj
  200. context[self.get_context_object_name(obj)] = obj
  201. cd = InstanceData(obj, request=request)
  202. if not cd.has_permission(request, self.action_name):
  203. raise PermissionDenied
  204. excludes = request.GET.get('exclude')
  205. if excludes:
  206. cd.excludes = excludes.split(",")
  207. self.object = cd
  208. context['object_data'] = cd
  209. context = self.get_context_data(**context)
  210. return self.render_to_response(context)
  211. def _get_form(request, model):
  212. default_relations_bare = request.GET.get('default')
  213. widgets = []
  214. default_relations = []
  215. default_relations_fields = []
  216. if default_relations_bare:
  217. default_relations_fields = default_relations_bare.split(",")
  218. default_relations = [(x, int(y)) for (x, y) in (f.split(":") for f in default_relations_fields)]
  219. default_relations_fields = [x for x, y in default_relations]
  220. for (x, y) in default_relations:
  221. f = FieldData(getattr(model, x).field, request)
  222. widgets.append((x, RapidRelationReadOnly(f.related_model())))
  223. ask_relations = []
  224. for f in ModelData(model).local_fields():
  225. if f.is_relation() and unicode(f.bare_name()) not in default_relations_fields:
  226. ask_relations.append(f)
  227. widgets += [(f.bare_name(), RapidSelector(f)) for f in ask_relations if f.related_model().is_controlled()]
  228. #ModelForm.Meta tem atributos com esses mesmos nomes,
  229. #então eu tenho que renomear.
  230. form_model = model
  231. form_widgets = dict(widgets)
  232. class CForm(ModelForm):
  233. def __init__(self, *args, **kwargs):
  234. initial = kwargs.get('initial', {})
  235. for (k, v) in default_relations:
  236. initial[k] = v
  237. kwargs['initial'] = initial
  238. super(CForm, self).__init__(*args, **kwargs)
  239. class Meta:
  240. model = form_model
  241. fields = '__all__'
  242. widgets = form_widgets
  243. return CForm
  244. class CreateView(generic.edit.CreateView):
  245. template_name = 'rapid/bare/create.html'
  246. action_name = ''
  247. object = None
  248. fields = '__all__'
  249. def request_form(self, request):
  250. return _get_form(request, self.model)
  251. def get(self, request, **kwargs):
  252. context = RequestContext(request).flatten()
  253. context.update(kwargs)
  254. cd = ModelData(self.model, request=request)
  255. if not cd.has_permission(request, self.action_name):
  256. raise PermissionDenied
  257. context['model_data'] = cd
  258. parent_model = self.model
  259. parent_fields = self.fields
  260. if request.POST:
  261. context['form'] = self.request_form(request)(request.POST)
  262. else:
  263. context['form'] = self.request_form(request)()
  264. context = self.get_context_data(**context)
  265. return self.render_to_response(context)
  266. def post(self, request, **kwargs):
  267. m = ModelData(self.model, request=request)
  268. if m.has_permission(request, self.action_name):
  269. f = self.request_form(request)(request.POST)
  270. if f.is_valid():
  271. f.save()
  272. return redirect(m.list_url())
  273. return self.get(request, **kwargs)
  274. raise PermissionDenied()
  275. class UpdateView(generic.edit.UpdateView):
  276. template_name = 'rapid/bare/update.html'
  277. action_name = ''
  278. fields = '__all__'
  279. def get_object(self, queryset=None):
  280. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  281. def request_form(self, request):
  282. return _get_form(request, self.model)
  283. def get(self, request, pk, **kwargs):
  284. context = RequestContext(request).flatten()
  285. context.update(kwargs)
  286. obj = self.get_object()
  287. context['object'] = obj
  288. context[self.get_context_object_name(obj)] = obj
  289. cd = InstanceData(obj, request=request)
  290. if not cd.has_permission(request, self.action_name):
  291. raise PermissionDenied
  292. self.object = cd
  293. context['object_data'] = cd
  294. if request.POST:
  295. context['form'] = self.request_form(request)(request.POST, instance=obj)
  296. else:
  297. context['form'] = self.request_form(request)(instance=obj)
  298. context = self.get_context_data(**context)
  299. return self.render_to_response(context)
  300. def post(self, request, pk, **kwargs):
  301. obj = self.get_object()
  302. m = InstanceData(obj, request=request)
  303. if m.has_permission(request, self.action_name):
  304. f = self.request_form(request)(request.POST, instance=obj)
  305. if f.is_valid():
  306. if f.instance.pk != obj.pk:
  307. raise PermissionDenied
  308. f.save()
  309. return redirect(m.list_url())
  310. self.form = f
  311. return self.get(request, pk, **kwargs)
  312. raise PermissionDenied
  313. class DeleteView(generic.edit.DeleteView):
  314. template_name = 'rapid/bare/delete.html'
  315. action_name = ''
  316. fields = '__all__'
  317. def __init__(self, **kwargs):
  318. super(DeleteView, self).__init__(**kwargs)
  319. self.success_url = ModelData(self.model).list_url()
  320. def get_object(self, queryset=None):
  321. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  322. def get(self, request, *args, **kwargs):
  323. obj = InstanceData(self.get_object())
  324. if not obj.has_permission(request, self.action_name):
  325. raise PermissionDenied
  326. return super(DeleteView, self).get(request, *args, **kwargs)
  327. def post(self, request, *args, **kwargs):
  328. obj = InstanceData(self.get_object())
  329. if not obj.has_permission(request, self.action_name):
  330. raise PermissionDenied
  331. return super(DeleteView, self).post(request, *args, **kwargs)
  332. class SelectView(ListView):
  333. """
  334. Apresenta uma lista selecionavel de objetos, para
  335. o preenchimento de relações.
  336. """
  337. template_name = 'rapid/bare/select.html'
  338. def bare_or_main(view):
  339. main_window_template = "rapid/main_window.html"
  340. def vw(request, *args, **kwargs):
  341. resp = view(request, *args, **kwargs)
  342. bare = is_ajax_request(request)
  343. if bare:
  344. if resp.status_code >= 300 and resp.status_code <= 399:
  345. # I'll interpret redirects as successful POST,
  346. # thus, the response'll get replaced by something with
  347. # a header that says "success!"
  348. ret = HttpResponse('')
  349. ret['X-FORM-STATUS:'] = 'success'
  350. return ret
  351. if not bare and resp.status_code == 200 and request.method != "HEAD":
  352. resp.render()
  353. body = resp.content
  354. main_win = loader.get_template(main_window_template)
  355. context = RequestContext(request)
  356. context.update({'body_text': body, 'this_url': request.get_full_path()})
  357. resp.content = main_win.render(context, request)
  358. return resp
  359. return vw
  360. def _rvw(view_class, action_name):
  361. view_class.action_name = action_name
  362. def vw(model):
  363. return bare_or_main(view_class.as_view(model=model))
  364. return vw
  365. default_actions = [
  366. (False, Action("list", "", [], _rvw(ListView, 'list'), "listar", "fa-list")),
  367. (False, Action("view", "(?P<pk>[0-9]+)", ['pk'], _rvw(ReadView, 'view'), "ver", "fa-eye")),
  368. (True, Action("edit", "(?P<pk>[0-9]+)", ['pk'], _rvw(UpdateView, 'edit'), "editar", "fa-pencil")),
  369. (True, Action("add", "", [], _rvw(CreateView, 'add'), "adicionar", "fa-plus")),
  370. (False, Action("select", "", [], _rvw(SelectView, 'select'), "selecionar", "fa-hand-o-up",
  371. visibility=Action.Visibility.hidden)),
  372. ]
  373. def register_model(model, name=None, read_set=None, write_set=None, can_erase=False):
  374. if not read_set:
  375. read_set = permissions.default_read(model)
  376. if not write_set:
  377. write_set = permissions.default_write(model)
  378. ret = []
  379. for edt, a in default_actions:
  380. if edt:
  381. ret.append(registry.register_action(a, MenuEntry(model, write_set, name=name)))
  382. else:
  383. ret.append(registry.register_action(a, MenuEntry(model, read_set, name=name)))
  384. if can_erase:
  385. a = Action("delete", "(?P<pk>[0-9]+)", ['pk'], _rvw(DeleteView, 'delete'), "apagar", "fa-trash-o")
  386. ret.append(registry.register_action(a, MenuEntry(model, write_set, name=name)))
  387. return [u for u in ret if u]
  388. def update_form_class(form):
  389. class F(UpdateView):
  390. def request_form(self, request):
  391. return form
  392. return F
  393. def register_instance_form(model, action_name, menu_name, form, permission_set, icon=None, visibility=None):
  394. if not icon:
  395. icon = ''
  396. a = Action(action_name, "(?P<pk>[0-9]+)", ['pk'], _rvw(update_form_class(form), action_name),
  397. menu_name, icon, visibility)
  398. u = registry.register_action(a, MenuEntry(model, permission_set))
  399. return [u] if u else []
  400. def register_simple_select(model, visible_fields, permission_set, name=None):
  401. class Vw(SelectView):
  402. fields = visible_fields
  403. a = Action("select", "", [], _rvw(Vw, 'select'), "selecionar", "fa-hand-o-up",
  404. visibility=Action.Visibility.hidden)
  405. u = registry.register_action(a, MenuEntry(model, permission_set, name=name))
  406. return [u] if u else []