views.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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 = 5
  40. number_of_middle_pages = 5
  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+1, self.page)):
  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):
  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 + 1, self.total_pages - e + 1)):
  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 + 1), self.total_pages + 1):
  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-1):self.registers_per_page*self.page]
  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. total = q.count()
  129. pagination.total_pages = int(math.ceil(total / pagination.registers_per_page))
  130. order = request.GET.get(self.order_param)
  131. if order:
  132. q = q.order_by(order)
  133. self.queryset = pagination.get_page(q)
  134. def values(self):
  135. for o in self.queryset:
  136. if self._fields:
  137. c = InstanceData(o, request=self.request, fields=self._fields)
  138. else:
  139. c = InstanceData(o, request=self.request)
  140. if c.has_permission(self.request, self.action_name):
  141. yield c
  142. def fields(self):
  143. order_param = self.order_param
  144. request = self.request
  145. class FieldParams:
  146. def __init__(self, field):
  147. self.field = field
  148. def order_up_url(self):
  149. return _replace_param(request, order_param, self.field.bare_name())
  150. def order_down_url(self):
  151. return _replace_param(request, order_param, u'-' + self.field.bare_name())
  152. def add_filter_url(self):
  153. pass
  154. def del_filter_url(self):
  155. pass
  156. for f in ModelData(self.model, request=self.request, fields=self._fields).fields():
  157. f.view = FieldParams(f)
  158. yield f
  159. def get(self, request, **kwargs):
  160. mdata = ModelData(self.model, request)
  161. if not mdata.has_permission(request, self.action_name):
  162. raise PermissionDenied
  163. #De forma similar ao get de ListView, recupera os objetos e verifica
  164. #se pode mostrar uma lista vazia.
  165. object_list = self.get_queryset()
  166. allow_empty = self.get_allow_empty()
  167. # Agora, popula o contexto com os request processors e os dados específicos
  168. # da listagem nos SA.
  169. context = RequestContext(request).flatten()
  170. context.update(kwargs)
  171. total_pages = int(math.ceil(object_list.count() / self.registers_per_page))
  172. page = int(request.GET.get('page', 1))
  173. p = self.Pagination(request, page, self.number_of_edge_pages,
  174. self.number_of_middle_pages, self.registers_per_page,
  175. total_pages)
  176. context['pages'] = p
  177. context['model'] = ModelData(self.model, request, fields=self.fields)
  178. context['view'] = self.View(request, self.action_name, self.model, object_list, self.fields, p)
  179. default_ordering = self.model._meta.ordering if hasattr(self.model._meta, 'ordering') else ''
  180. ordering = request.GET.get('order', default_ordering)
  181. offset = (page - 1) * self.registers_per_page
  182. self.object_list = object_list[offset: offset + self.registers_per_page]
  183. context[self.get_context_object_name(object_list)] = object_list
  184. context['object_list'] = object_list
  185. context = self.get_context_data(**context)
  186. self.object_list = object_list
  187. return self.render_to_response(context)
  188. class ReadView(generic.detail.DetailView):
  189. template_name = 'rapid/bare/detail.html'
  190. action_name = ''
  191. request = None
  192. def get_object(self, request=None):
  193. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  194. def get(self, request, pk, **kwargs):
  195. context = RequestContext(request).flatten()
  196. context.update(kwargs)
  197. obj = self.get_object(self.request)
  198. context['object'] = obj
  199. context[self.get_context_object_name(obj)] = obj
  200. cd = InstanceData(obj, request=request)
  201. if not cd.has_permission(request, self.action_name):
  202. raise PermissionDenied
  203. excludes = request.GET.get('exclude')
  204. if excludes:
  205. cd.excludes = excludes.split(",")
  206. self.object = cd
  207. context['object_data'] = cd
  208. context = self.get_context_data(**context)
  209. return self.render_to_response(context)
  210. def _get_form(request, model):
  211. default_relations_bare = request.GET.get('default')
  212. widgets = []
  213. default_relations = []
  214. default_relations_fields = []
  215. if default_relations_bare:
  216. default_relations_fields = default_relations_bare.split(",")
  217. default_relations = [(x, int(y)) for (x, y) in (f.split(":") for f in default_relations_fields)]
  218. default_relations_fields = [x for x, y in default_relations]
  219. for (x, y) in default_relations:
  220. f = FieldData(getattr(model, x).field, request)
  221. widgets.append((x, RapidRelationReadOnly(f.related_model())))
  222. ask_relations = []
  223. for f in ModelData(model).local_fields():
  224. if f.is_relation() and unicode(f.bare_name()) not in default_relations_fields:
  225. ask_relations.append(f)
  226. widgets += [(f.bare_name(), RapidSelector(f)) for f in ask_relations if f.related_model().has_permission(request, 'select')]
  227. #ModelForm.Meta tem atributos com esses mesmos nomes,
  228. #então eu tenho que renomear.
  229. form_model = model
  230. form_widgets = dict(widgets)
  231. class CForm(ModelForm):
  232. def __init__(self, *args, **kwargs):
  233. initial = kwargs.get('initial', {})
  234. for (k, v) in default_relations:
  235. initial[k] = v
  236. kwargs['initial'] = initial
  237. super(CForm, self).__init__(*args, **kwargs)
  238. class Meta:
  239. model = form_model
  240. fields = '__all__'
  241. widgets = form_widgets
  242. return CForm
  243. class CreateView(generic.edit.CreateView):
  244. template_name = 'rapid/bare/create.html'
  245. action_name = ''
  246. object = None
  247. fields = '__all__'
  248. def request_form(self, request):
  249. return _get_form(request, self.model)
  250. def get(self, request, **kwargs):
  251. context = RequestContext(request).flatten()
  252. context.update(kwargs)
  253. cd = ModelData(self.model, request=request)
  254. if not cd.has_permission(request, self.action_name):
  255. raise PermissionDenied
  256. context['model_data'] = cd
  257. parent_model = self.model
  258. parent_fields = self.fields
  259. if request.POST:
  260. context['form'] = self.request_form(request)(request.POST, request.FILEs)
  261. else:
  262. context['form'] = self.request_form(request)()
  263. context = self.get_context_data(**context)
  264. return self.render_to_response(context)
  265. def post(self, request, **kwargs):
  266. m = ModelData(self.model, request=request)
  267. if m.has_permission(request, self.action_name):
  268. f = self.request_form(request)(request.POST, request.FILES)
  269. if f.is_valid():
  270. f.save()
  271. return redirect(m.list_url())
  272. return self.get(request, **kwargs)
  273. raise PermissionDenied()
  274. class UpdateView(generic.edit.UpdateView):
  275. template_name = 'rapid/bare/update.html'
  276. action_name = ''
  277. fields = '__all__'
  278. def get_object(self, queryset=None):
  279. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  280. def request_form(self, request):
  281. return _get_form(request, self.model)
  282. def get(self, request, pk, **kwargs):
  283. context = RequestContext(request).flatten()
  284. context.update(kwargs)
  285. obj = self.get_object()
  286. context['object'] = obj
  287. context[self.get_context_object_name(obj)] = obj
  288. cd = InstanceData(obj, request=request)
  289. if not cd.has_permission(request, self.action_name):
  290. raise PermissionDenied
  291. self.object = cd
  292. context['object_data'] = cd
  293. if request.POST:
  294. context['form'] = self.request_form(request)(request.POST, request.FILES, instance=obj)
  295. else:
  296. context['form'] = self.request_form(request)(instance=obj)
  297. context = self.get_context_data(**context)
  298. return self.render_to_response(context)
  299. def post(self, request, pk, **kwargs):
  300. obj = self.get_object()
  301. m = InstanceData(obj, request=request)
  302. if m.has_permission(request, self.action_name):
  303. f = self.request_form(request)(request.POST, request.FILES, instance=obj)
  304. if f.is_valid():
  305. if f.instance.pk != obj.pk:
  306. raise PermissionDenied
  307. f.save()
  308. return redirect(m.list_url())
  309. self.form = f
  310. return self.get(request, pk, **kwargs)
  311. raise PermissionDenied
  312. class DeleteView(generic.edit.DeleteView):
  313. template_name = 'rapid/bare/delete.html'
  314. action_name = ''
  315. fields = '__all__'
  316. def __init__(self, **kwargs):
  317. super(DeleteView, self).__init__(**kwargs)
  318. self.success_url = ModelData(self.model).list_url()
  319. def get_object(self, queryset=None):
  320. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  321. def get(self, request, *args, **kwargs):
  322. obj = InstanceData(self.get_object())
  323. if not obj.has_permission(request, self.action_name):
  324. raise PermissionDenied
  325. return super(DeleteView, self).get(request, *args, **kwargs)
  326. def post(self, request, *args, **kwargs):
  327. obj = InstanceData(self.get_object())
  328. if not obj.has_permission(request, self.action_name):
  329. raise PermissionDenied
  330. return super(DeleteView, self).post(request, *args, **kwargs)
  331. class SelectView(ListView):
  332. """
  333. Apresenta uma lista selecionavel de objetos, para
  334. o preenchimento de relações.
  335. """
  336. template_name = 'rapid/bare/select.html'
  337. def bare_or_main(view):
  338. main_window_template = "rapid/main_window.html"
  339. def vw(request, *args, **kwargs):
  340. resp = view(request, *args, **kwargs)
  341. bare = is_ajax_request(request)
  342. if bare:
  343. if resp.status_code >= 300 and resp.status_code <= 399:
  344. # I'll interpret redirects as successful POST,
  345. # thus, the response'll get replaced by something with
  346. # a header that says "success!"
  347. ret = HttpResponse('')
  348. ret['X-FORM-STATUS:'] = 'success'
  349. return ret
  350. if not bare and resp.status_code == 200 and request.method != "HEAD":
  351. resp.render()
  352. body = resp.content
  353. main_win = loader.get_template(main_window_template)
  354. context = RequestContext(request)
  355. context.update({'body_text': body, 'this_url': request.get_full_path()})
  356. resp.content = main_win.render(context, request)
  357. return resp
  358. return vw
  359. def update_form_class(form):
  360. class F(UpdateView):
  361. def request_form(self, request):
  362. return form
  363. return F