views.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  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. if len(self.object_list) > 0:
  184. context['has_objects'] = True
  185. context['first_object'] = self.object_list[0]
  186. else:
  187. context['has_objects'] = False
  188. context[self.get_context_object_name(object_list)] = object_list
  189. context['object_list'] = object_list
  190. context = self.get_context_data(**context)
  191. self.object_list = object_list
  192. return self.render_to_response(context)
  193. class ReadView(generic.detail.DetailView):
  194. template_name = 'rapid/bare/detail.html'
  195. action_name = ''
  196. request = None
  197. def get_object(self, request=None):
  198. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  199. def get(self, request, pk, **kwargs):
  200. context = RequestContext(request).flatten()
  201. context.update(kwargs)
  202. obj = self.get_object(self.request)
  203. context['object'] = obj
  204. context[self.get_context_object_name(obj)] = obj
  205. cd = InstanceData(obj, request=request)
  206. if not cd.has_permission(request, self.action_name):
  207. raise PermissionDenied
  208. excludes = request.GET.get('exclude')
  209. if excludes:
  210. cd.excludes = excludes.split(",")
  211. self.object = cd
  212. context['object_data'] = cd
  213. context = self.get_context_data(**context)
  214. return self.render_to_response(context)
  215. def _get_form(request, model):
  216. default_relations_bare = request.GET.get('default')
  217. widgets = []
  218. default_relations = []
  219. default_relations_fields = []
  220. if default_relations_bare:
  221. default_relations_fields = default_relations_bare.split(",")
  222. default_relations = [(x, int(y)) for (x, y) in (f.split(":") for f in default_relations_fields)]
  223. default_relations_fields = [x for x, y in default_relations]
  224. for (x, y) in default_relations:
  225. f = FieldData(getattr(model, x).field, request)
  226. widgets.append((x, RapidRelationReadOnly(f.related_model())))
  227. ask_relations = []
  228. for f in ModelData(model).local_fields():
  229. if f.is_relation() and unicode(f.bare_name()) not in default_relations_fields:
  230. ask_relations.append(f)
  231. widgets += [(f.bare_name(), RapidSelector(f)) for f in ask_relations if f.related_model().has_permission(request, 'select')]
  232. #ModelForm.Meta tem atributos com esses mesmos nomes,
  233. #então eu tenho que renomear.
  234. form_model = model
  235. form_widgets = dict(widgets)
  236. class CForm(ModelForm):
  237. def __init__(self, *args, **kwargs):
  238. initial = kwargs.get('initial', {})
  239. for (k, v) in default_relations:
  240. initial[k] = v
  241. kwargs['initial'] = initial
  242. super(CForm, self).__init__(*args, **kwargs)
  243. class Meta:
  244. model = form_model
  245. fields = '__all__'
  246. widgets = form_widgets
  247. return CForm
  248. class CreateView(generic.edit.CreateView):
  249. template_name = 'rapid/bare/create.html'
  250. action_name = ''
  251. object = None
  252. fields = '__all__'
  253. def request_form(self, request):
  254. return _get_form(request, self.model)
  255. def get(self, request, **kwargs):
  256. context = RequestContext(request).flatten()
  257. context.update(kwargs)
  258. cd = ModelData(self.model, request=request)
  259. if not cd.has_permission(request, self.action_name):
  260. raise PermissionDenied
  261. context['model_data'] = cd
  262. parent_model = self.model
  263. parent_fields = self.fields
  264. if request.POST:
  265. context['form'] = self.request_form(request)(request.POST, request.FILEs)
  266. else:
  267. context['form'] = self.request_form(request)()
  268. context = self.get_context_data(**context)
  269. return self.render_to_response(context)
  270. def post(self, request, **kwargs):
  271. m = ModelData(self.model, request=request)
  272. if m.has_permission(request, self.action_name):
  273. f = self.request_form(request)(request.POST, request.FILES)
  274. if f.is_valid():
  275. f.save()
  276. return redirect(m.list_url())
  277. return self.get(request, **kwargs)
  278. raise PermissionDenied()
  279. class UpdateView(generic.edit.UpdateView):
  280. template_name = 'rapid/bare/update.html'
  281. action_name = ''
  282. fields = '__all__'
  283. def get_object(self, queryset=None):
  284. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  285. def request_form(self, request):
  286. return _get_form(request, self.model)
  287. def get(self, request, pk, **kwargs):
  288. context = RequestContext(request).flatten()
  289. context.update(kwargs)
  290. obj = self.get_object()
  291. context['object'] = obj
  292. context[self.get_context_object_name(obj)] = obj
  293. cd = InstanceData(obj, request=request)
  294. if not cd.has_permission(request, self.action_name):
  295. raise PermissionDenied
  296. self.object = cd
  297. context['object_data'] = cd
  298. if request.POST:
  299. context['form'] = self.request_form(request)(request.POST, request.FILES, instance=obj)
  300. else:
  301. context['form'] = self.request_form(request)(instance=obj)
  302. context = self.get_context_data(**context)
  303. return self.render_to_response(context)
  304. def post(self, request, pk, **kwargs):
  305. obj = self.get_object()
  306. m = InstanceData(obj, request=request)
  307. if m.has_permission(request, self.action_name):
  308. f = self.request_form(request)(request.POST, request.FILES, instance=obj)
  309. if f.is_valid():
  310. if f.instance.pk != obj.pk:
  311. raise PermissionDenied
  312. f.save()
  313. return redirect(m.list_url())
  314. self.form = f
  315. return self.get(request, pk, **kwargs)
  316. raise PermissionDenied
  317. class DeleteView(generic.edit.DeleteView):
  318. template_name = 'rapid/bare/delete.html'
  319. action_name = ''
  320. fields = '__all__'
  321. def __init__(self, **kwargs):
  322. super(DeleteView, self).__init__(**kwargs)
  323. self.success_url = ModelData(self.model).list_url()
  324. def get_object(self, queryset=None):
  325. return get_object_or_404(self.model, pk=self.kwargs['pk'])
  326. def get(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).get(request, *args, **kwargs)
  331. def post(self, request, *args, **kwargs):
  332. obj = InstanceData(self.get_object())
  333. if not obj.has_permission(request, self.action_name):
  334. raise PermissionDenied
  335. return super(DeleteView, self).post(request, *args, **kwargs)
  336. class SelectView(ListView):
  337. """
  338. Apresenta uma lista selecionavel de objetos, para
  339. o preenchimento de relações.
  340. """
  341. template_name = 'rapid/bare/select.html'
  342. def bare_or_main(view):
  343. main_window_template = "rapid/main_window.html"
  344. def vw(request, *args, **kwargs):
  345. resp = view(request, *args, **kwargs)
  346. bare = is_ajax_request(request)
  347. if bare:
  348. if resp.status_code >= 300 and resp.status_code <= 399:
  349. # I'll interpret redirects as successful POST,
  350. # thus, the response'll get replaced by something with
  351. # a header that says "success!"
  352. ret = HttpResponse('')
  353. ret['X-FORM-STATUS:'] = 'success'
  354. return ret
  355. if not bare and resp.status_code == 200 and request.method != "HEAD":
  356. resp.render()
  357. body = resp.content
  358. main_win = loader.get_template(main_window_template)
  359. context = RequestContext(request)
  360. context.update({'body_text': body, 'this_url': request.get_full_path()})
  361. resp.content = main_win.render(context, request)
  362. return resp
  363. return vw
  364. def update_form_class(form):
  365. class F(UpdateView):
  366. def request_form(self, request):
  367. return form
  368. return F