views.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  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. def get_page(self, queryset):
  115. return queryset[self.registers_per_page*self.page:self.registers_per_page*(self.page+1)-1]
  116. class View:
  117. order_param = 'order'
  118. def __init__(self, request, action_name, model, queryset, fields, pagination):
  119. self.request = request
  120. self.model = model
  121. self.action_name = action_name
  122. self._fields = fields
  123. q = queryset
  124. self.filters = FilterSet.from_request(ModelData(self.model), request)
  125. if self.filters:
  126. q = q.filter(**self.filters.query_dict())
  127. self.queryset = q
  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. #De forma similar ao get de ListView, recupera os objetos e verifica
  162. #se pode mostrar uma lista vazia.
  163. object_list = self.get_queryset()
  164. allow_empty = self.get_allow_empty()
  165. # Agora, popula o contexto com os request processors e os dados específicos
  166. # da listagem nos SA.
  167. context = RequestContext(request).flatten()
  168. context.update(kwargs)
  169. total_pages = int(math.ceil(object_list.count() / self.registers_per_page)) + 1
  170. page = int(request.GET.get('page', 1))
  171. p = self.Pagination(request, page, self.number_of_edge_pages,
  172. self.number_of_middle_pages, self.registers_per_page,
  173. total_pages)
  174. context['pages'] = p
  175. context['model'] = ModelData(self.model, request, fields=self.fields)
  176. context['view'] = self.View(request, self.action_name, self.model, object_list, self.fields, p)
  177. default_ordering = self.model._meta.ordering if hasattr(self.model._meta, 'ordering') else ''
  178. ordering = request.GET.get('order', default_ordering)
  179. offset = (page - 1) * self.registers_per_page
  180. self.object_list = object_list[offset: offset + self.registers_per_page]
  181. if len(self.object_list) > 0:
  182. context['has_objects'] = True
  183. context['first_object'] = self.object_list[0]
  184. else:
  185. context['has_objects'] = False
  186. context[self.get_context_object_name(object_list)] = object_list
  187. context['object_list'] = object_list
  188. context = self.get_context_data(**context)
  189. self.object_list = object_list
  190. return self.render_to_response(context)
  191. class ReadView(generic.detail.DetailView):
  192. template_name = 'rapid/bare/detail.html'
  193. action_name = ''
  194. request = None
  195. def get_object(self, request=None):
  196. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  197. def get(self, request, pk, **kwargs):
  198. context = RequestContext(request).flatten()
  199. context.update(kwargs)
  200. obj = self.get_object(self.request)
  201. context['object'] = obj
  202. context[self.get_context_object_name(obj)] = obj
  203. cd = InstanceData(obj, request=request)
  204. if not cd.has_permission(request, self.action_name):
  205. raise PermissionDenied
  206. excludes = request.GET.get('exclude')
  207. if excludes:
  208. cd.excludes = excludes.split(",")
  209. self.object = cd
  210. context['object_data'] = cd
  211. context = self.get_context_data(**context)
  212. return self.render_to_response(context)
  213. def _get_form(request, model):
  214. default_relations_bare = request.GET.get('default')
  215. widgets = []
  216. default_relations = []
  217. default_relations_fields = []
  218. if default_relations_bare:
  219. default_relations_fields = default_relations_bare.split(",")
  220. default_relations = [(x, int(y)) for (x, y) in (f.split(":") for f in default_relations_fields)]
  221. default_relations_fields = [x for x, y in default_relations]
  222. for (x, y) in default_relations:
  223. f = FieldData(getattr(model, x).field, request)
  224. widgets.append((x, RapidRelationReadOnly(f.related_model())))
  225. ask_relations = []
  226. for f in ModelData(model).local_fields():
  227. if f.is_relation() and unicode(f.bare_name()) not in default_relations_fields:
  228. ask_relations.append(f)
  229. widgets += [(f.bare_name(), RapidSelector(f)) for f in ask_relations if f.related_model().is_controlled()]
  230. #ModelForm.Meta tem atributos com esses mesmos nomes,
  231. #então eu tenho que renomear.
  232. form_model = model
  233. form_widgets = dict(widgets)
  234. class CForm(ModelForm):
  235. def __init__(self, *args, **kwargs):
  236. initial = kwargs.get('initial', {})
  237. for (k, v) in default_relations:
  238. initial[k] = v
  239. kwargs['initial'] = initial
  240. super(CForm, self).__init__(*args, **kwargs)
  241. class Meta:
  242. model = form_model
  243. fields = '__all__'
  244. widgets = form_widgets
  245. return CForm
  246. class CreateView(generic.edit.CreateView):
  247. template_name = 'rapid/bare/create.html'
  248. action_name = ''
  249. object = None
  250. fields = '__all__'
  251. def request_form(self, request):
  252. return _get_form(request, self.model)
  253. def get(self, request, **kwargs):
  254. context = RequestContext(request).flatten()
  255. context.update(kwargs)
  256. cd = ModelData(self.model, request=request)
  257. if not cd.has_permission(request, self.action_name):
  258. raise PermissionDenied
  259. context['model_data'] = cd
  260. parent_model = self.model
  261. parent_fields = self.fields
  262. if request.POST:
  263. context['form'] = self.request_form(request)(request.POST)
  264. else:
  265. context['form'] = self.request_form(request)()
  266. context = self.get_context_data(**context)
  267. return self.render_to_response(context)
  268. def post(self, request, **kwargs):
  269. m = ModelData(self.model, request=request)
  270. if m.has_permission(request, self.action_name):
  271. f = self.request_form(request)(request.POST)
  272. if f.is_valid():
  273. f.save()
  274. return redirect(m.list_url())
  275. return self.get(request, **kwargs)
  276. raise PermissionDenied()
  277. class UpdateView(generic.edit.UpdateView):
  278. template_name = 'rapid/bare/update.html'
  279. action_name = ''
  280. fields = '__all__'
  281. def get_object(self, queryset=None):
  282. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  283. def request_form(self, request):
  284. return _get_form(request, self.model)
  285. def get(self, request, pk, **kwargs):
  286. context = RequestContext(request).flatten()
  287. context.update(kwargs)
  288. obj = self.get_object()
  289. context['object'] = obj
  290. context[self.get_context_object_name(obj)] = obj
  291. cd = InstanceData(obj, request=request)
  292. if not cd.has_permission(request, self.action_name):
  293. raise PermissionDenied
  294. self.object = cd
  295. context['object_data'] = cd
  296. if request.POST:
  297. context['form'] = self.request_form(request)(request.POST, instance=obj)
  298. else:
  299. context['form'] = self.request_form(request)(instance=obj)
  300. context = self.get_context_data(**context)
  301. return self.render_to_response(context)
  302. def post(self, request, pk, **kwargs):
  303. obj = self.get_object()
  304. m = InstanceData(obj, request=request)
  305. if m.has_permission(request, self.action_name):
  306. f = self.request_form(request)(request.POST, instance=obj)
  307. if f.is_valid():
  308. if f.instance.pk != obj.pk:
  309. raise PermissionDenied
  310. f.save()
  311. return redirect(m.list_url())
  312. self.form = f
  313. return self.get(request, pk, **kwargs)
  314. raise PermissionDenied
  315. class DeleteView(generic.edit.DeleteView):
  316. template_name = 'rapid/bare/delete.html'
  317. action_name = ''
  318. fields = '__all__'
  319. def __init__(self, **kwargs):
  320. super(DeleteView, self).__init__(**kwargs)
  321. self.success_url = ModelData(self.model).list_url()
  322. def get_object(self, queryset=None):
  323. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  324. def get(self, request, *args, **kwargs):
  325. obj = InstanceData(self.get_object())
  326. if not obj.has_permission(request, self.action_name):
  327. raise PermissionDenied
  328. return super(DeleteView, self).get(request, *args, **kwargs)
  329. def post(self, request, *args, **kwargs):
  330. obj = InstanceData(self.get_object())
  331. if not obj.has_permission(request, self.action_name):
  332. raise PermissionDenied
  333. return super(DeleteView, self).post(request, *args, **kwargs)
  334. class SelectView(ListView):
  335. """
  336. Apresenta uma lista selecionavel de objetos, para
  337. o preenchimento de relações.
  338. """
  339. template_name = 'rapid/bare/select.html'
  340. def bare_or_main(view):
  341. main_window_template = "rapid/main_window.html"
  342. def vw(request, *args, **kwargs):
  343. resp = view(request, *args, **kwargs)
  344. bare = is_ajax_request(request)
  345. if bare:
  346. if resp.status_code >= 300 and resp.status_code <= 399:
  347. # I'll interpret redirects as successful POST,
  348. # thus, the response'll get replaced by something with
  349. # a header that says "success!"
  350. ret = HttpResponse('')
  351. ret['X-FORM-STATUS:'] = 'success'
  352. return ret
  353. if not bare and resp.status_code == 200 and request.method != "HEAD":
  354. resp.render()
  355. body = resp.content
  356. main_win = loader.get_template(main_window_template)
  357. context = RequestContext(request)
  358. context.update({'body_text': body, 'this_url': request.get_full_path()})
  359. resp.content = main_win.render(context, request)
  360. return resp
  361. return vw
  362. def _rvw(view_class, action_name):
  363. view_class.action_name = action_name
  364. def vw(model):
  365. return bare_or_main(view_class.as_view(model=model))
  366. return vw
  367. default_actions = [
  368. (False, Action("list", "", [], _rvw(ListView, 'list'), "listar", "fa-list")),
  369. (False, Action("view", "(?P<pk>[0-9]+)", ['pk'], _rvw(ReadView, 'view'), "ver", "fa-eye")),
  370. (True, Action("edit", "(?P<pk>[0-9]+)", ['pk'], _rvw(UpdateView, 'edit'), "editar", "fa-pencil")),
  371. (True, Action("add", "", [], _rvw(CreateView, 'add'), "adicionar", "fa-plus")),
  372. (False, Action("select", "", [], _rvw(SelectView, 'select'), "selecionar", "fa-hand-o-up",
  373. visibility=Action.Visibility.hidden)),
  374. ]
  375. def register_model(model, name=None, read_set=None, write_set=None, can_erase=False):
  376. if not read_set:
  377. read_set = permissions.default_read(model)
  378. if not write_set:
  379. write_set = permissions.default_write(model)
  380. ret = []
  381. for edt, a in default_actions:
  382. if edt:
  383. ret.append(registry.register_action(a, MenuEntry(model, write_set, name=name)))
  384. else:
  385. ret.append(registry.register_action(a, MenuEntry(model, read_set, name=name)))
  386. if can_erase:
  387. a = Action("delete", "(?P<pk>[0-9]+)", ['pk'], _rvw(DeleteView, 'delete'), "apagar", "fa-trash-o")
  388. ret.append(registry.register_action(a, MenuEntry(model, write_set, name=name)))
  389. return [u for u in ret if u]
  390. def update_form_class(form):
  391. class F(UpdateView):
  392. def request_form(self, request):
  393. return form
  394. return F
  395. def register_instance_form(model, action_name, menu_name, form, permission_set, icon=None, visibility=None):
  396. if not icon:
  397. icon = ''
  398. a = Action(action_name, "(?P<pk>[0-9]+)", ['pk'], _rvw(update_form_class(form), action_name),
  399. menu_name, icon, visibility)
  400. u = registry.register_action(a, MenuEntry(model, permission_set))
  401. return [u] if u else []
  402. def register_simple_select(model, visible_fields, permission_set, name=None):
  403. class Vw(SelectView):
  404. fields = visible_fields
  405. a = Action("select", "", [], _rvw(Vw, 'select'), "selecionar", "fa-hand-o-up",
  406. visibility=Action.Visibility.hidden)
  407. u = registry.register_action(a, MenuEntry(model, permission_set, name=name))
  408. return [u] if u else []