registry.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. from django.core.urlresolvers import reverse
  2. from django.db.utils import OperationalError
  3. from rapid.models import Application
  4. from django.conf.urls import url
  5. import inspect
  6. import logging
  7. from os import path
  8. def _split_all_path(file_name):
  9. file_name = path.splitdrive(file_name)[1]
  10. p = 'a'
  11. while p:
  12. file_name, p = path.split(file_name)
  13. yield p
  14. def _caller_urls_module():
  15. st = inspect.stack()
  16. for rec in st:
  17. file_name = rec[1]
  18. segments = list(_split_all_path(file_name))
  19. i = 0
  20. for i in xrange(0, len(segments) - 2):
  21. p = path.normcase(segments[i])
  22. p = path.splitext(p)[0]
  23. if p == "urls":
  24. return segments[i+1]
  25. return None
  26. def _model_name(model):
  27. if hasattr(model, "url_name"):
  28. return model.url_name
  29. return model._meta.verbose_name
  30. class MenuEntry:
  31. """
  32. The data that goes on a menu item.
  33. Model, permissions and url
  34. """
  35. def __init__(self, model, permission, url_name=None):
  36. self.url_name = url_name
  37. self.model = model
  38. self.permission = permission
  39. def get_url(self, instance=None):
  40. ats = [(x, getattr(instance, x)) for x in self.action.query_parameters]
  41. if self.action.pick_generator:
  42. return [(n, reverse(self.url, kwargs=dict(ats + p))) for
  43. n, p in self.action.pick_generator()]
  44. return reverse(self.url, kwargs=dict(ats))
  45. def __unicode__(self):
  46. return u"Menu entry: " + self.url_name + " -> " + unicode(self.action)
  47. def __str__(self):
  48. return str(unicode(self))
  49. class ModuleEntry:
  50. """
  51. Module data used at menu construction
  52. """
  53. def __init__(self, python_name, menu_name):
  54. self.python_name = python_name
  55. self.menu_name = menu_name
  56. self.models = set()
  57. class Action:
  58. """
  59. An action to be done over a model.
  60. Default actions are "list", "view", "edit", "add", "delete", and "select",
  61. those are defined at the views module.
  62. """
  63. def __init__(self, name, url_parameters, query_parameters, view_factory,
  64. verbose_name=None, icon=None, visibility=None, pick_generator=None,
  65. overlay=None):
  66. self.name = name
  67. self.url_parameters = url_parameters
  68. self.query_parameters = query_parameters
  69. self.view_factory = view_factory
  70. self.verbose_name = verbose_name if verbose_name else name
  71. self.icon = icon
  72. self.visibility = self.Visibility.details if visibility is None else visibility
  73. self.pick_generator = pick_generator
  74. self.overlay = overlay if overlay else self.Overlay.better_in_overlay
  75. def __unicode__(self):
  76. return u"Action: " + self.name
  77. def __str__(self):
  78. return str(unicode(self))
  79. class Visibility(object):
  80. hidden = 1
  81. details = 2
  82. list = 3
  83. class Overlay(object):
  84. better_in_overlay = 'better-in-overlay'
  85. this_overlay = ''
  86. clear_overlays = 'clear-overlays'
  87. class _Registry:
  88. """
  89. Registry of URLs, models and views present on the menu
  90. The registry must:
  91. -- for the menu creation
  92. list registered modules
  93. list registered models
  94. list menu entries by module
  95. list actions by model
  96. reverse url of action and model
  97. -- for crud generation
  98. query if model is registered
  99. list actions by model
  100. reverse url of action and model
  101. """
  102. def __init__(self):
  103. """
  104. Populates the menu registry
  105. """
  106. self._modules = {} # ModuleEntry by python_name
  107. self._models = {} # {'action name': MenuEntry} by model class
  108. try:
  109. for a in Application.objects.filter(enabled=True):
  110. m = ModuleEntry(a.python_name, a.name)
  111. self._modules[a.python_name] = m
  112. except OperationalError:
  113. #Should always get fixed by a migration
  114. logging.error("Can not query applications table. You may need to run \"manage.py migrate\".")
  115. def register_action(self, action, entry, **kwargs):
  116. """
  117. Registers an action at this registry, so it will appear on the menu
  118. and can be reversed at the cruds.
  119. :param action: Action type
  120. :param entry: The menu entry where it will appear
  121. :param kwargs: Arguments (besides model) that'll be passed to the view_factory of the action
  122. :return: A Django URL pattern that should be added to the patterns of a urls.py module
  123. """
  124. from django.contrib.auth.models import User
  125. module_name = _caller_urls_module()
  126. model = entry.model
  127. if not module_name:
  128. raise Exception("Unidentified python module registering " + str(model))
  129. if not registry._modules.has_key(module_name):
  130. logging.error("Module " + module_name + " is not set-up for registering cruds")
  131. return None
  132. module_entry = registry._modules[module_name]
  133. module_entry.models.add(model)
  134. if not entry.url_name:
  135. entry.url_name = _model_name(model)
  136. entry.action = action
  137. entry_url = module_entry.menu_name + '_' + entry.url_name + '_' + action.name
  138. entry.url = entry_url
  139. model_actions = self._models.get(model, {})
  140. if model_actions.has_key(action.name):
  141. raise Exception("Action " + action.name + " already registered for model " + str(model))
  142. model_actions[action.name] = entry
  143. self._models[model] = model_actions
  144. url_path = r'^%s/%s' % (entry.url_name, action.name)
  145. if action.url_parameters:
  146. url_path += '/%s' % action.url_parameters
  147. return url(url_path+'/?$', action.view_factory(model=entry.model, **kwargs), name=entry_url)
  148. def get_url_of_action(self, model, action_name, **kwargs):
  149. acts = self._models.get(model)
  150. if acts and acts.has_key(action_name):
  151. return reverse(acts[action_name].url, kwargs=kwargs)
  152. def modules(self):
  153. return self._modules.values()
  154. def entry_names(self):
  155. return [x.menu_name for x in self._modules.values()]
  156. def is_controlled(self, model):
  157. return self._models.has_key(model)
  158. def model_entry(self, model):
  159. return self._models.get(model)
  160. def module_models(self, module):
  161. return self._modules[module].models
  162. registry = _Registry()