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 8.5KB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import os
  2. from importlib import import_module
  3. from django.core.exceptions import ImproperlyConfigured
  4. from django.utils.module_loading import module_has_submodule
  5. MODELS_MODULE_NAME = 'models'
  6. class AppConfig:
  7. """Class representing a Django application and its configuration."""
  8. def __init__(self, app_name, app_module):
  9. # Full Python path to the application e.g. 'django.contrib.admin'.
  10. self.name = app_name
  11. # Root module for the application e.g. <module 'django.contrib.admin'
  12. # from 'django/contrib/admin/__init__.py'>.
  13. self.module = app_module
  14. # Reference to the Apps registry that holds this AppConfig. Set by the
  15. # registry when it registers the AppConfig instance.
  16. self.apps = None
  17. # The following attributes could be defined at the class level in a
  18. # subclass, hence the test-and-set pattern.
  19. # Last component of the Python path to the application e.g. 'admin'.
  20. # This value must be unique across a Django project.
  21. if not hasattr(self, 'label'):
  22. self.label = app_name.rpartition(".")[2]
  23. # Human-readable name for the application e.g. "Admin".
  24. if not hasattr(self, 'verbose_name'):
  25. self.verbose_name = self.label.title()
  26. # Filesystem path to the application directory e.g.
  27. # '/path/to/django/contrib/admin'.
  28. if not hasattr(self, 'path'):
  29. self.path = self._path_from_module(app_module)
  30. # Module containing models e.g. <module 'django.contrib.admin.models'
  31. # from 'django/contrib/admin/models.py'>. Set by import_models().
  32. # None if the application doesn't have a models module.
  33. self.models_module = None
  34. # Mapping of lowercase model names to model classes. Initially set to
  35. # None to prevent accidental access before import_models() runs.
  36. self.models = None
  37. def __repr__(self):
  38. return '<%s: %s>' % (self.__class__.__name__, self.label)
  39. def _path_from_module(self, module):
  40. """Attempt to determine app's filesystem path from its module."""
  41. # See #21874 for extended discussion of the behavior of this method in
  42. # various cases.
  43. # Convert paths to list because Python's _NamespacePath doesn't support
  44. # indexing.
  45. paths = list(getattr(module, '__path__', []))
  46. if len(paths) != 1:
  47. filename = getattr(module, '__file__', None)
  48. if filename is not None:
  49. paths = [os.path.dirname(filename)]
  50. else:
  51. # For unknown reasons, sometimes the list returned by __path__
  52. # contains duplicates that must be removed (#25246).
  53. paths = list(set(paths))
  54. if len(paths) > 1:
  55. raise ImproperlyConfigured(
  56. "The app module %r has multiple filesystem locations (%r); "
  57. "you must configure this app with an AppConfig subclass "
  58. "with a 'path' class attribute." % (module, paths))
  59. elif not paths:
  60. raise ImproperlyConfigured(
  61. "The app module %r has no filesystem location, "
  62. "you must configure this app with an AppConfig subclass "
  63. "with a 'path' class attribute." % (module,))
  64. return paths[0]
  65. @classmethod
  66. def create(cls, entry):
  67. """
  68. Factory that creates an app config from an entry in INSTALLED_APPS.
  69. """
  70. try:
  71. # If import_module succeeds, entry is a path to an app module,
  72. # which may specify an app config class with default_app_config.
  73. # Otherwise, entry is a path to an app config class or an error.
  74. module = import_module(entry)
  75. except ImportError:
  76. # Track that importing as an app module failed. If importing as an
  77. # app config class fails too, we'll trigger the ImportError again.
  78. module = None
  79. mod_path, _, cls_name = entry.rpartition('.')
  80. # Raise the original exception when entry cannot be a path to an
  81. # app config class.
  82. if not mod_path:
  83. raise
  84. else:
  85. try:
  86. # If this works, the app module specifies an app config class.
  87. entry = module.default_app_config
  88. except AttributeError:
  89. # Otherwise, it simply uses the default app config class.
  90. return cls(entry, module)
  91. else:
  92. mod_path, _, cls_name = entry.rpartition('.')
  93. # If we're reaching this point, we must attempt to load the app config
  94. # class located at <mod_path>.<cls_name>
  95. mod = import_module(mod_path)
  96. try:
  97. cls = getattr(mod, cls_name)
  98. except AttributeError:
  99. if module is None:
  100. # If importing as an app module failed, check if the module
  101. # contains any valid AppConfigs and show them as choices.
  102. # Otherwise, that error probably contains the most informative
  103. # traceback, so trigger it again.
  104. candidates = sorted(
  105. repr(name) for name, candidate in mod.__dict__.items()
  106. if isinstance(candidate, type) and
  107. issubclass(candidate, AppConfig) and
  108. candidate is not AppConfig
  109. )
  110. if candidates:
  111. raise ImproperlyConfigured(
  112. "'%s' does not contain a class '%s'. Choices are: %s."
  113. % (mod_path, cls_name, ', '.join(candidates))
  114. )
  115. import_module(entry)
  116. else:
  117. raise
  118. # Check for obvious errors. (This check prevents duck typing, but
  119. # it could be removed if it became a problem in practice.)
  120. if not issubclass(cls, AppConfig):
  121. raise ImproperlyConfigured(
  122. "'%s' isn't a subclass of AppConfig." % entry)
  123. # Obtain app name here rather than in AppClass.__init__ to keep
  124. # all error checking for entries in INSTALLED_APPS in one place.
  125. try:
  126. app_name = cls.name
  127. except AttributeError:
  128. raise ImproperlyConfigured(
  129. "'%s' must supply a name attribute." % entry)
  130. # Ensure app_name points to a valid module.
  131. try:
  132. app_module = import_module(app_name)
  133. except ImportError:
  134. raise ImproperlyConfigured(
  135. "Cannot import '%s'. Check that '%s.%s.name' is correct." % (
  136. app_name, mod_path, cls_name,
  137. )
  138. )
  139. # Entry is a path to an app config class.
  140. return cls(app_name, app_module)
  141. def get_model(self, model_name, require_ready=True):
  142. """
  143. Return the model with the given case-insensitive model_name.
  144. Raise LookupError if no model exists with this name.
  145. """
  146. if require_ready:
  147. self.apps.check_models_ready()
  148. else:
  149. self.apps.check_apps_ready()
  150. try:
  151. return self.models[model_name.lower()]
  152. except KeyError:
  153. raise LookupError(
  154. "App '%s' doesn't have a '%s' model." % (self.label, model_name))
  155. def get_models(self, include_auto_created=False, include_swapped=False):
  156. """
  157. Return an iterable of models.
  158. By default, the following models aren't included:
  159. - auto-created models for many-to-many relations without
  160. an explicit intermediate table,
  161. - models that have been swapped out.
  162. Set the corresponding keyword argument to True to include such models.
  163. Keyword arguments aren't documented; they're a private API.
  164. """
  165. self.apps.check_models_ready()
  166. for model in self.models.values():
  167. if model._meta.auto_created and not include_auto_created:
  168. continue
  169. if model._meta.swapped and not include_swapped:
  170. continue
  171. yield model
  172. def import_models(self):
  173. # Dictionary of models for this app, primarily maintained in the
  174. # 'all_models' attribute of the Apps this AppConfig is attached to.
  175. self.models = self.apps.all_models[self.label]
  176. if module_has_submodule(self.module, MODELS_MODULE_NAME):
  177. models_module_name = '%s.%s' % (self.name, MODELS_MODULE_NAME)
  178. self.models_module = import_module(models_module_name)
  179. def ready(self):
  180. """
  181. Override this method in subclasses to run code when Django starts.
  182. """