registry.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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. return reverse(self.url, kwargs=dict(ats))
  42. def __unicode__(self):
  43. return u"Menu entry: " + self.url_name + " -> " + unicode(self.action)
  44. def __str__(self):
  45. return str(unicode(self))
  46. class ModuleEntry:
  47. """
  48. Module data used at menu construction
  49. """
  50. def __init__(self, python_name, menu_name):
  51. self.python_name = python_name
  52. self.menu_name = menu_name
  53. self.models = set()
  54. class Action:
  55. """
  56. An action to be done over a model.
  57. Default actions are "list", "view", "edit", "add", "delete", and "select",
  58. those are defined at the views module.
  59. """
  60. def __init__(self, name, url_parameters, query_parameters, view_factory,
  61. verbose_name=None, icon=None, visibility=None):
  62. self.name = name
  63. self.url_parameters = url_parameters
  64. self.query_parameters = query_parameters
  65. self.view_factory = view_factory
  66. self.verbose_name = verbose_name if verbose_name else name
  67. self.icon = icon
  68. self.visibility = self.Visibility.details if visibility is None else visibility
  69. def __unicode__(self):
  70. return u"Action: " + self.name
  71. def __str__(self):
  72. return str(unicode(self))
  73. class Visibility:
  74. hidden = 1
  75. details = 2
  76. list = 3
  77. class _Registry:
  78. """
  79. Registry of URLs, models and views present on the menu
  80. The registry must:
  81. -- for the menu creation
  82. list registered modules
  83. list registered models
  84. list menu entries by module
  85. list actions by model
  86. reverse url of action and model
  87. -- for crud generation
  88. query if model is registered
  89. list actions by model
  90. reverse url of action and model
  91. """
  92. def __init__(self):
  93. """
  94. Populates the menu registry
  95. """
  96. self._modules = {} # ModuleEntry by python_name
  97. self._models = {} # {'action name': MenuEntry} by model class
  98. try:
  99. for a in Application.objects.filter(enabled=True):
  100. m = ModuleEntry(a.python_name, a.name)
  101. self._modules[a.python_name] = m
  102. except OperationalError:
  103. #Should always get fixed by a migration
  104. logging.error("Can not query applications table. You may need to run \"manage.py migrate\".")
  105. def register_action(self, action, entry, **kwargs):
  106. """
  107. Registers an action at this registry, so it will appear on the menu
  108. and can be reversed at the cruds.
  109. :param action: Action type
  110. :param entry: The menu entry where it will appear
  111. :param kwargs: Arguments (besides model) that'll be passed to the view_factory of the action
  112. :return: A Django URL pattern that should be added to the patterns of a urls.py module
  113. """
  114. from django.contrib.auth.models import User
  115. module_name = _caller_urls_module()
  116. model = entry.model
  117. if not module_name:
  118. raise Exception("Unidentified python module registering " + str(model))
  119. if not registry._modules.has_key(module_name):
  120. logging.error("Module " + module_name + " is not set-up for registering cruds")
  121. return None
  122. module_entry = registry._modules[module_name]
  123. module_entry.models.add(model)
  124. if not entry.url_name:
  125. entry.url_name = _model_name(model)
  126. entry.action = action
  127. entry_url = module_entry.menu_name + '_' + entry.url_name + '_' + action.name
  128. entry.url = entry_url
  129. model_actions = self._models.get(model, {})
  130. if model_actions.has_key(action.name):
  131. raise Exception("Action " + action.name + " already registered for model " + str(model))
  132. model_actions[action.name] = entry
  133. self._models[model] = model_actions
  134. return url(r'^%s/%s/%s$' % (entry.url_name, action.name, action.url_parameters),
  135. action.view_factory(model=entry.model, **kwargs), name=entry_url)
  136. def get_url_of_action(self, model, action_name, **kwargs):
  137. acts = self._models.get(model)
  138. if acts and acts.has_key(action_name):
  139. return reverse(acts[action_name].url, kwargs=kwargs)
  140. def modules(self):
  141. return self._modules.values()
  142. def entry_names(self):
  143. return [x.menu_name for x in self._modules.values()]
  144. def is_controlled(self, model):
  145. return self._models.has_key(model)
  146. def model_entry(self, model):
  147. return self._models.get(model)
  148. def module_models(self, module):
  149. return self._modules[module].models
  150. registry = _Registry()