wrappers.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import
  3. from __future__ import division
  4. from __future__ import print_function
  5. from __future__ import unicode_literals
  6. from django.contrib.contenttypes.models import ContentType
  7. from django.utils.encoding import force_text
  8. from rapid.registry import registry, Action
  9. from rapid import filters
  10. import itertools
  11. from django.db import models
  12. from rapid import permissions
  13. class InstanceData(object):
  14. def __init__(self, instance, request=None, excludes=None, creator=None, fields=None):
  15. excludes = [] if excludes is None else excludes
  16. self.model = ModelData(type(instance), request, excludes, creator, fields)
  17. self.instance = instance
  18. self.request = request
  19. self.excludes = excludes if excludes else []
  20. self.creator = creator
  21. self._fields = self.model.fields()
  22. def values(self):
  23. r = []
  24. o = self.instance
  25. for f in self.model.fields():
  26. if f.is_relation:
  27. r.append(self._value_of_field(o, f))
  28. else:
  29. r.append(self._value_of_field(o, f))
  30. return r
  31. def _value_of_field(self, instance, field):
  32. """
  33. Returns the value of the given field.
  34. ::return A tuple, with the actual value of the field, a boolean that is true iff
  35. the value is iterable, and a sequence of URLs for the viewers of the instances
  36. of the first element, or False if there are no viewers.
  37. """
  38. if hasattr(instance, field.accessor_name()):
  39. v = getattr(instance, field.accessor_name())
  40. else: # Many to many relations without value may disappear
  41. return [], True
  42. if hasattr(v, '__iter__'):
  43. return (v, ()), True
  44. if hasattr(v, 'all'):
  45. return [(x, InstanceData(x, self.request, creator=(self, field))) for x in v.all()], True
  46. if isinstance(v, models.Model):
  47. return (v, InstanceData(v, self.request, creator=(self, field))), False
  48. return (v, ()), False
  49. def fields_and_values(self):
  50. r = []
  51. for field in self.model.fields():
  52. value, is_multiple = self._value_of_field(self.instance, field)
  53. r.append((field, value, is_multiple))
  54. return r
  55. def is_controlled(self):
  56. return self.model.is_controlled()
  57. def can_read(self):
  58. return self.has_permission(self.request, 'view')
  59. def can_write(self):
  60. return self.has_permission(self.request, 'edit')
  61. def view_url(self):
  62. return registry.get_url_of_action(self.model.model, "view", pk=self.instance.pk)
  63. def edit_url(self):
  64. url = registry.get_url_of_action(self.model.model, "edit", pk=self.instance.pk)
  65. by = self.creator
  66. if by:
  67. dt, fd = by
  68. if fd.one_to_one or fd.one_to_many:
  69. # Do not change the parent of the viewer.
  70. return url + "?default=" + fd.field.name + ":" + str(dt.object.pk)
  71. if fd.many_to_one or fd.many_to_many:
  72. return url
  73. return url
  74. def remove_url(self):
  75. return registry.get_url_of_action(self.model.model, "delete", pk=self.instance.pk)
  76. def create_url(self):
  77. return registry.get_url_of_action(self.model.model, "add")
  78. def list_url(self):
  79. return registry.get_url_of_action(self.model.model, "list")
  80. def select_url(self):
  81. return registry.get_url_of_action(self.model.model, "select")
  82. def actions(self):
  83. r = []
  84. acts = registry.model_entry(self.model.model)
  85. if self.request and acts:
  86. for a in acts.values():
  87. if self.has_permission(self.request, a.action.name) and\
  88. a.action.visibility > Action.Visibility.hidden:
  89. r.append((a, a.get_url(self.instance)))
  90. return r
  91. def model_actions(self):
  92. r = []
  93. for (a, u) in self.actions():
  94. if not a.action.query_parameters:
  95. r.append((a, u))
  96. return r
  97. def instance_actions(self):
  98. r = []
  99. for (a, u) in self.actions():
  100. if a.action.query_parameters:
  101. r.append((a, u))
  102. return r
  103. def list_actions(self):
  104. r = []
  105. for (a, u) in self.instance_actions():
  106. if a.action.visibility == Action.Visibility.list:
  107. r.append((a, u))
  108. return r
  109. def has_permission(self, request, action_name):
  110. m = registry.model_entry(self.model.model).get(action_name)
  111. if m:
  112. perm = m.permission.instances
  113. return permissions.has_instance(self.model, perm(request), self.instance)
  114. return False
  115. def __unicode__(self):
  116. return force_text(self.instance)
  117. def __str__(self):
  118. return str(self.model) + ': ' + str(self.instance.pk)
  119. class ModelData(object):
  120. def __init__(self, model, request=None, excludes=None, creator=None, fields=None):
  121. excludes = [] if excludes is None else excludes
  122. self.model = model
  123. self.request = request
  124. self.excludes = excludes if excludes else []
  125. self.creator = creator
  126. self._fields = [self.field_by_name(f) for f in fields] if fields else self.all_fields()
  127. def model_name(self):
  128. # noinspection PyProtectedMember
  129. return force_text(self.model._meta.verbose_name)
  130. def model_name_plural(self):
  131. # noinspection PyProtectedMember
  132. return force_text(self.model._meta.verbose_name_plural)
  133. def default_manager(self):
  134. # noinspection PyProtectedMember
  135. return self.model._default_manager
  136. def all_fields(self):
  137. r = []
  138. relations = []
  139. ignore_fields = []
  140. for f in itertools.chain(self.local_fields(), self.related_fields()):
  141. if f.bare_name() in ignore_fields:
  142. ignore_fields.remove(f.bare_name())
  143. continue
  144. if hasattr(f.field, 'is_rapid_alternatives') and f.field.is_rapid_alternatives:
  145. for ff in self.rapid_alternative_data():
  146. if ff[1].ct_field == f.bare_name():
  147. f = FieldData(ff[1])
  148. for fr in r:
  149. if fr.bare_name() == ff[1].fk_field:
  150. r.remove(fr)
  151. ignore_fields.append(ff[1].fk_field)
  152. break
  153. if f.is_relation():
  154. relations.append(f)
  155. else:
  156. if f.name not in self.excludes:
  157. r.append(f)
  158. for f in relations:
  159. if f.name not in self.excludes:
  160. r.append(f)
  161. return r
  162. def fields(self):
  163. return self._fields
  164. def local_fields(self):
  165. """
  166. :return: FieldData for each local (not related) field of this model.
  167. """
  168. r = []
  169. # noinspection PyProtectedMember
  170. for f in self.model._meta.local_fields:
  171. if f.name not in self.excludes:
  172. r.append(FieldData(f, self.request))
  173. # noinspection PyProtectedMember
  174. for f in self.model._meta.local_many_to_many:
  175. if f.name not in self.excludes:
  176. r.append(FieldData(f, self.request))
  177. return r
  178. def rapid_alternative_data(self):
  179. """
  180. :return: Tuples of the form (name, field) with the
  181. AlternativeData fields of this model.
  182. Those are not returned by fields()
  183. """
  184. for k, v in self.model.__dict__.items():
  185. if hasattr(v, 'is_rapid_alternatives') and v.is_rapid_alternatives:
  186. yield (k, v)
  187. def related_fields(self):
  188. """
  189. :return: A FieldData for each related manager for this model.
  190. """
  191. # noinspection PyProtectedMember
  192. return [FieldData(f, self.request) for f in self.model._meta.get_all_related_objects()]
  193. def is_controlled(self):
  194. """
  195. :return: True if this model is already controlled by the rapid registry.
  196. """
  197. return registry.is_controlled(self.model)
  198. def can_read(self):
  199. if self.can_write():
  200. return True
  201. vw = registry.model_entry(self.model)['view'].permission(self.request)
  202. if vw:
  203. return vw.exists()
  204. return False
  205. def can_write(self):
  206. ed = registry.model_entry(self.model)['edit'].permission(self.request)
  207. if ed:
  208. return ed.exists()
  209. return False
  210. def create_url(self):
  211. """
  212. :return: URL of the "add" action.
  213. """
  214. return registry.get_url_of_action(self.model, "add")
  215. def list_url(self):
  216. """
  217. :return: URL of the "list" action.
  218. """
  219. return registry.get_url_of_action(self.model, "list")
  220. def select_url(self):
  221. """
  222. :return: URL of the "select" action.
  223. """
  224. return registry.get_url_of_action(self.model, "select")
  225. def actions(self):
  226. """
  227. :return: All actions available for this model (with the object's request).
  228. """
  229. r = []
  230. acts = registry.model_entry(self.model)
  231. if self.request and acts:
  232. for a in acts.values():
  233. if self.has_permission(self.request, a.action.name) and\
  234. not a.action.query_parameters and\
  235. a.action.visibility > Action.Visibility.hidden:
  236. r.append((a, a.get_url()))
  237. return r
  238. def has_permission(self, request, action_name):
  239. m = registry.model_entry(self.model)
  240. if m:
  241. a = m.get(action_name)
  242. if a:
  243. return bool(a.permission.model(request))
  244. return False
  245. def field_by_name(self, field_name):
  246. """
  247. :return: A FieldData with the field of this model that has the given name.
  248. """
  249. # noinspection PyProtectedMember
  250. return FieldData(self.model._meta.get_field(field_name), self.request)
  251. def content_type(self):
  252. """
  253. :return: The Django registry ContentType for this model.
  254. """
  255. return ContentType.objects.get_for_model(self.model)
  256. def __unicode__(self):
  257. return force_text(self.model)
  258. def __str__(self):
  259. return 'Model: ' + str(self.model)
  260. class FieldData(object):
  261. def __init__(self, field, request=None):
  262. self.field = field
  263. self.request = request
  264. @classmethod
  265. def from_model(cls, model, field_name):
  266. ff = ModelData(model).fields()
  267. for f in ff:
  268. if f.bare_name() == force_text(field_name):
  269. return f
  270. return None
  271. def bare_name(self):
  272. return force_text(self.field.name)
  273. def accessor_name(self):
  274. if hasattr(self.field, 'get_accessor_name'):
  275. return force_text(self.field.get_accessor_name())
  276. return force_text(self.field.name)
  277. def name(self):
  278. if self.is_auto() and self.is_relation():
  279. return self.related_model().model_name_plural() + ' - ' + self.related_field().name()
  280. if hasattr(self.field, "verbose_name"):
  281. return force_text(self.field.verbose_name)
  282. return force_text(self.field.name)
  283. def name_plural(self):
  284. if self.is_auto() and self.is_relation():
  285. return self.related_model().model_name_plural() + ' - ' + self.related_field().name_plural()
  286. if hasattr(self.field, "verbose_name_plural"):
  287. return force_text(self.field.verbose_name_plural)
  288. return self.name() + "s"
  289. def is_relation(self):
  290. return self.field.is_relation
  291. def is_multiple(self):
  292. if not self.is_relation():
  293. return False
  294. if self.field.one_to_many:
  295. return True
  296. if self.field.many_to_many:
  297. return True
  298. return False
  299. def related_model(self):
  300. if hasattr(self.field, "related_model"):
  301. return ModelData(self.field.related_model)
  302. if hasattr(self.field, "to"):
  303. return ModelData(self.field.to)
  304. return None
  305. def related_field(self):
  306. if hasattr(self.field, "field"):
  307. return FieldData(self.field.field, self.request)
  308. return None
  309. def is_auto(self):
  310. return self.field.auto_created
  311. def is_existential_dependency(self):
  312. """
  313. :return: True if this field determine an existential dependency relation between this model
  314. and another one.
  315. That is, returns true if, because of this field, one element of this model can only exist if
  316. there is one element of one other model.
  317. Example: A required ForeignKey.
  318. """
  319. if not self.is_relation():
  320. return False
  321. f = self.field
  322. if hasattr(f, "many_to_many") and f.many_to_many:
  323. return False
  324. if hasattr(f, "many_to_one") and self.field.many_to_one:
  325. return False
  326. if hasattr(self.field, "required"):
  327. return self.field.required
  328. return True
  329. def filter_html(self):
  330. return filters.Filter.selection_type_html(self, self.request)
  331. def __str__(self):
  332. return self.bare_name()
  333. class ValueData(object):
  334. def __init__(self, value, field):
  335. self.value = value
  336. self.field = field
  337. def can_view(self):
  338. if self.field.is_relation():
  339. o = self.field.related_model()
  340. return registry.is_controlled(o)
  341. return False
  342. def is_multiple(self):
  343. return self.field.is_multiple()
  344. def __str__(self):
  345. return str(self.field) + ': ' + str(self.value)