Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

config.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. import inspect
  2. import os
  3. from importlib import import_module
  4. from django.core.exceptions import ImproperlyConfigured
  5. from django.utils.functional import cached_property
  6. from django.utils.module_loading import import_string, module_has_submodule
  7. APPS_MODULE_NAME = "apps"
  8. MODELS_MODULE_NAME = "models"
  9. class AppConfig:
  10. """Class representing a Django application and its configuration."""
  11. def __init__(self, app_name, app_module):
  12. # Full Python path to the application e.g. 'django.contrib.admin'.
  13. self.name = app_name
  14. # Root module for the application e.g. <module 'django.contrib.admin'
  15. # from 'django/contrib/admin/__init__.py'>.
  16. self.module = app_module
  17. # Reference to the Apps registry that holds this AppConfig. Set by the
  18. # registry when it registers the AppConfig instance.
  19. self.apps = None
  20. # The following attributes could be defined at the class level in a
  21. # subclass, hence the test-and-set pattern.
  22. # Last component of the Python path to the application e.g. 'admin'.
  23. # This value must be unique across a Django project.
  24. if not hasattr(self, "label"):
  25. self.label = app_name.rpartition(".")[2]
  26. if not self.label.isidentifier():
  27. raise ImproperlyConfigured(
  28. "The app label '%s' is not a valid Python identifier." % self.label
  29. )
  30. # Human-readable name for the application e.g. "Admin".
  31. if not hasattr(self, "verbose_name"):
  32. self.verbose_name = self.label.title()
  33. # Filesystem path to the application directory e.g.
  34. # '/path/to/django/contrib/admin'.
  35. if not hasattr(self, "path"):
  36. self.path = self._path_from_module(app_module)
  37. # Module containing models e.g. <module 'django.contrib.admin.models'
  38. # from 'django/contrib/admin/models.py'>. Set by import_models().
  39. # None if the application doesn't have a models module.
  40. self.models_module = None
  41. # Mapping of lowercase model names to model classes. Initially set to
  42. # None to prevent accidental access before import_models() runs.
  43. self.models = None
  44. def __repr__(self):
  45. return "<%s: %s>" % (self.__class__.__name__, self.label)
  46. @cached_property
  47. def default_auto_field(self):
  48. from django.conf import settings
  49. return settings.DEFAULT_AUTO_FIELD
  50. @property
  51. def _is_default_auto_field_overridden(self):
  52. return self.__class__.default_auto_field is not AppConfig.default_auto_field
  53. def _path_from_module(self, module):
  54. """Attempt to determine app's filesystem path from its module."""
  55. # See #21874 for extended discussion of the behavior of this method in
  56. # various cases.
  57. # Convert to list because __path__ may not support indexing.
  58. paths = list(getattr(module, "__path__", []))
  59. if len(paths) != 1:
  60. filename = getattr(module, "__file__", None)
  61. if filename is not None:
  62. paths = [os.path.dirname(filename)]
  63. else:
  64. # For unknown reasons, sometimes the list returned by __path__
  65. # contains duplicates that must be removed (#25246).
  66. paths = list(set(paths))
  67. if len(paths) > 1:
  68. raise ImproperlyConfigured(
  69. "The app module %r has multiple filesystem locations (%r); "
  70. "you must configure this app with an AppConfig subclass "
  71. "with a 'path' class attribute." % (module, paths)
  72. )
  73. elif not paths:
  74. raise ImproperlyConfigured(
  75. "The app module %r has no filesystem location, "
  76. "you must configure this app with an AppConfig subclass "
  77. "with a 'path' class attribute." % module
  78. )
  79. return paths[0]
  80. @classmethod
  81. def create(cls, entry):
  82. """
  83. Factory that creates an app config from an entry in INSTALLED_APPS.
  84. """
  85. # create() eventually returns app_config_class(app_name, app_module).
  86. app_config_class = None
  87. app_name = None
  88. app_module = None
  89. # If import_module succeeds, entry points to the app module.
  90. try:
  91. app_module = import_module(entry)
  92. except Exception:
  93. pass
  94. else:
  95. # If app_module has an apps submodule that defines a single
  96. # AppConfig subclass, use it automatically.
  97. # To prevent this, an AppConfig subclass can declare a class
  98. # variable default = False.
  99. # If the apps module defines more than one AppConfig subclass,
  100. # the default one can declare default = True.
  101. if module_has_submodule(app_module, APPS_MODULE_NAME):
  102. mod_path = "%s.%s" % (entry, APPS_MODULE_NAME)
  103. mod = import_module(mod_path)
  104. # Check if there's exactly one AppConfig candidate,
  105. # excluding those that explicitly define default = False.
  106. app_configs = [
  107. (name, candidate)
  108. for name, candidate in inspect.getmembers(mod, inspect.isclass)
  109. if (
  110. issubclass(candidate, cls)
  111. and candidate is not cls
  112. and getattr(candidate, "default", True)
  113. )
  114. ]
  115. if len(app_configs) == 1:
  116. app_config_class = app_configs[0][1]
  117. else:
  118. # Check if there's exactly one AppConfig subclass,
  119. # among those that explicitly define default = True.
  120. app_configs = [
  121. (name, candidate)
  122. for name, candidate in app_configs
  123. if getattr(candidate, "default", False)
  124. ]
  125. if len(app_configs) > 1:
  126. candidates = [repr(name) for name, _ in app_configs]
  127. raise RuntimeError(
  128. "%r declares more than one default AppConfig: "
  129. "%s." % (mod_path, ", ".join(candidates))
  130. )
  131. elif len(app_configs) == 1:
  132. app_config_class = app_configs[0][1]
  133. # Use the default app config class if we didn't find anything.
  134. if app_config_class is None:
  135. app_config_class = cls
  136. app_name = entry
  137. # If import_string succeeds, entry is an app config class.
  138. if app_config_class is None:
  139. try:
  140. app_config_class = import_string(entry)
  141. except Exception:
  142. pass
  143. # If both import_module and import_string failed, it means that entry
  144. # doesn't have a valid value.
  145. if app_module is None and app_config_class is None:
  146. # If the last component of entry starts with an uppercase letter,
  147. # then it was likely intended to be an app config class; if not,
  148. # an app module. Provide a nice error message in both cases.
  149. mod_path, _, cls_name = entry.rpartition(".")
  150. if mod_path and cls_name[0].isupper():
  151. # We could simply re-trigger the string import exception, but
  152. # we're going the extra mile and providing a better error
  153. # message for typos in INSTALLED_APPS.
  154. # This may raise ImportError, which is the best exception
  155. # possible if the module at mod_path cannot be imported.
  156. mod = import_module(mod_path)
  157. candidates = [
  158. repr(name)
  159. for name, candidate in inspect.getmembers(mod, inspect.isclass)
  160. if issubclass(candidate, cls) and candidate is not cls
  161. ]
  162. msg = "Module '%s' does not contain a '%s' class." % (
  163. mod_path,
  164. cls_name,
  165. )
  166. if candidates:
  167. msg += " Choices are: %s." % ", ".join(candidates)
  168. raise ImportError(msg)
  169. else:
  170. # Re-trigger the module import exception.
  171. import_module(entry)
  172. # Check for obvious errors. (This check prevents duck typing, but
  173. # it could be removed if it became a problem in practice.)
  174. if not issubclass(app_config_class, AppConfig):
  175. raise ImproperlyConfigured("'%s' isn't a subclass of AppConfig." % entry)
  176. # Obtain app name here rather than in AppClass.__init__ to keep
  177. # all error checking for entries in INSTALLED_APPS in one place.
  178. if app_name is None:
  179. try:
  180. app_name = app_config_class.name
  181. except AttributeError:
  182. raise ImproperlyConfigured("'%s' must supply a name attribute." % entry)
  183. # Ensure app_name points to a valid module.
  184. try:
  185. app_module = import_module(app_name)
  186. except ImportError:
  187. raise ImproperlyConfigured(
  188. "Cannot import '%s'. Check that '%s.%s.name' is correct."
  189. % (
  190. app_name,
  191. app_config_class.__module__,
  192. app_config_class.__qualname__,
  193. )
  194. )
  195. # Entry is a path to an app config class.
  196. return app_config_class(app_name, app_module)
  197. def get_model(self, model_name, require_ready=True):
  198. """
  199. Return the model with the given case-insensitive model_name.
  200. Raise LookupError if no model exists with this name.
  201. """
  202. if require_ready:
  203. self.apps.check_models_ready()
  204. else:
  205. self.apps.check_apps_ready()
  206. try:
  207. return self.models[model_name.lower()]
  208. except KeyError:
  209. raise LookupError(
  210. "App '%s' doesn't have a '%s' model." % (self.label, model_name)
  211. )
  212. def get_models(self, include_auto_created=False, include_swapped=False):
  213. """
  214. Return an iterable of models.
  215. By default, the following models aren't included:
  216. - auto-created models for many-to-many relations without
  217. an explicit intermediate table,
  218. - models that have been swapped out.
  219. Set the corresponding keyword argument to True to include such models.
  220. Keyword arguments aren't documented; they're a private API.
  221. """
  222. self.apps.check_models_ready()
  223. for model in self.models.values():
  224. if model._meta.auto_created and not include_auto_created:
  225. continue
  226. if model._meta.swapped and not include_swapped:
  227. continue
  228. yield model
  229. def import_models(self):
  230. # Dictionary of models for this app, primarily maintained in the
  231. # 'all_models' attribute of the Apps this AppConfig is attached to.
  232. self.models = self.apps.all_models[self.label]
  233. if module_has_submodule(self.module, MODELS_MODULE_NAME):
  234. models_module_name = "%s.%s" % (self.name, MODELS_MODULE_NAME)
  235. self.models_module = import_module(models_module_name)
  236. def ready(self):
  237. """
  238. Override this method in subclasses to run code when Django starts.
  239. """