registry.py 5.7 KB

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