1
from collections import Counter, defaultdict, OrderedDict
7
from django.core.exceptions import AppRegistryNotReady, ImproperlyConfigured
8
from django.utils import lru_cache
9
from django.utils.deprecation import RemovedInDjango19Warning
10
from django.utils._os import upath
12
from .config import AppConfig
17
A registry that stores the configuration of installed applications.
19
It also keeps track of models eg. to provide reverse-relations.
22
def __init__(self, installed_apps=()):
23
# installed_apps is set to None when creating the master registry
24
# because it cannot be populated at that point. Other registries must
25
# provide a list of installed apps and are populated immediately.
26
if installed_apps is None and hasattr(sys.modules[__name__], 'apps'):
27
raise RuntimeError("You must supply an installed_apps argument.")
29
# Mapping of app labels => model names => model classes. Every time a
30
# model is imported, ModelBase.__new__ calls apps.register_model which
31
# creates an entry in all_models. All imported models are registered,
32
# regardless of whether they're defined in an installed application
33
# and whether the registry has been populated. Since it isn't possible
34
# to reimport a module safely (it could reexecute initialization code)
35
# all_models is never overridden or reset.
36
self.all_models = defaultdict(OrderedDict)
38
# Mapping of labels to AppConfig instances for installed apps.
39
self.app_configs = OrderedDict()
41
# Stack of app_configs. Used to store the current state in
42
# set_available_apps and set_installed_apps.
43
self.stored_app_configs = []
45
# Whether the registry is populated.
46
self.apps_ready = self.models_ready = self.ready = False
48
# Lock for thread-safe population.
49
self._lock = threading.Lock()
51
# Pending lookups for lazy relations.
52
self._pending_lookups = {}
54
# Populate apps and models, unless it's the master registry.
55
if installed_apps is not None:
56
self.populate(installed_apps)
58
def populate(self, installed_apps=None):
60
Loads application configurations and models.
62
This method imports each application module and then each model module.
64
It is thread safe and idempotent, but not reentrant.
69
# populate() might be called by two threads in parallel on servers
70
# that create threads before initializing the WSGI callable.
75
# app_config should be pristine, otherwise the code below won't
76
# guarantee that the order matches the order in INSTALLED_APPS.
78
raise RuntimeError("populate() isn't reentrant")
80
# Load app configs and app modules.
81
for entry in installed_apps:
82
if isinstance(entry, AppConfig):
85
app_config = AppConfig.create(entry)
86
if app_config.label in self.app_configs:
87
raise ImproperlyConfigured(
88
"Application labels aren't unique, "
89
"duplicates: %s" % app_config.label)
91
self.app_configs[app_config.label] = app_config
93
# Check for duplicate app names.
95
app_config.name for app_config in self.app_configs.values())
97
name for name, count in counts.most_common() if count > 1]
99
raise ImproperlyConfigured(
100
"Application names aren't unique, "
101
"duplicates: %s" % ", ".join(duplicates))
103
self.apps_ready = True
106
for app_config in self.app_configs.values():
107
all_models = self.all_models[app_config.label]
108
app_config.import_models(all_models)
112
self.models_ready = True
114
for app_config in self.get_app_configs():
119
def check_apps_ready(self):
121
Raises an exception if all apps haven't been imported yet.
123
if not self.apps_ready:
124
raise AppRegistryNotReady("Apps aren't loaded yet.")
126
def check_models_ready(self):
128
Raises an exception if all models haven't been imported yet.
130
if not self.models_ready:
131
raise AppRegistryNotReady("Models aren't loaded yet.")
133
def get_app_configs(self):
135
Imports applications and returns an iterable of app configs.
137
self.check_apps_ready()
138
return self.app_configs.values()
140
def get_app_config(self, app_label):
142
Imports applications and returns an app config for the given label.
144
Raises LookupError if no application exists with this label.
146
self.check_apps_ready()
148
return self.app_configs[app_label]
150
raise LookupError("No installed app with label '%s'." % app_label)
152
# This method is performance-critical at least for Django's test suite.
153
@lru_cache.lru_cache(maxsize=None)
154
def get_models(self, app_mod=None, include_auto_created=False,
155
include_deferred=False, include_swapped=False):
157
Returns a list of all installed models.
159
By default, the following models aren't included:
161
- auto-created models for many-to-many relations without
162
an explicit intermediate table,
163
- models created to satisfy deferred attribute queries,
164
- models that have been swapped out.
166
Set the corresponding keyword argument to True to include such models.
168
self.check_models_ready()
171
"The app_mod argument of get_models is deprecated.",
172
RemovedInDjango19Warning, stacklevel=2)
173
app_label = app_mod.__name__.split('.')[-2]
175
return list(self.get_app_config(app_label).get_models(
176
include_auto_created, include_deferred, include_swapped))
181
for app_config in self.app_configs.values():
182
result.extend(list(app_config.get_models(
183
include_auto_created, include_deferred, include_swapped)))
186
def get_model(self, app_label, model_name=None):
188
Returns the model matching the given app_label and model_name.
190
As a shortcut, this function also accepts a single argument in the
191
form <app_label>.<model_name>.
193
model_name is case-insensitive.
195
Raises LookupError if no application exists with this label, or no
196
model exists with this name in the application. Raises ValueError if
197
called with a single argument that doesn't contain exactly one dot.
199
self.check_models_ready()
200
if model_name is None:
201
app_label, model_name = app_label.split('.')
202
return self.get_app_config(app_label).get_model(model_name.lower())
204
def register_model(self, app_label, model):
205
# Since this method is called when models are imported, it cannot
206
# perform imports because of the risk of import loops. It mustn't
207
# call get_app_config().
208
model_name = model._meta.model_name
209
app_models = self.all_models[app_label]
210
if model_name in app_models:
212
"Conflicting '%s' models in application '%s': %s and %s." %
213
(model_name, app_label, app_models[model_name], model))
214
app_models[model_name] = model
217
def is_installed(self, app_name):
219
Checks whether an application with this name exists in the registry.
221
app_name is the full name of the app eg. 'django.contrib.admin'.
223
self.check_apps_ready()
224
return any(ac.name == app_name for ac in self.app_configs.values())
226
def get_containing_app_config(self, object_name):
228
Look for an app config containing a given object.
230
object_name is the dotted Python path to the object.
232
Returns the app config for the inner application in case of nesting.
233
Returns None if the object isn't in any registered app config.
235
# In Django 1.7 and 1.8, it's allowed to call this method at import
236
# time, even while the registry is being populated. In Django 1.9 and
237
# later, that should be forbidden with `self.check_apps_ready()`.
239
for app_config in self.app_configs.values():
240
if object_name.startswith(app_config.name):
241
subpath = object_name[len(app_config.name):]
242
if subpath == '' or subpath[0] == '.':
243
candidates.append(app_config)
245
return sorted(candidates, key=lambda ac: -len(ac.name))[0]
247
def get_registered_model(self, app_label, model_name):
249
Similar to get_model(), but doesn't require that an app exists with
252
It's safe to call this method at import time, even while the registry
255
model = self.all_models[app_label].get(model_name.lower())
258
"Model '%s.%s' not registered." % (app_label, model_name))
261
def set_available_apps(self, available):
263
Restricts the set of installed apps used by get_app_config[s].
265
available must be an iterable of application names.
267
set_available_apps() must be balanced with unset_available_apps().
269
Primarily used for performance optimization in TransactionTestCase.
271
This method is safe is the sense that it doesn't trigger any imports.
273
available = set(available)
274
installed = set(app_config.name for app_config in self.get_app_configs())
275
if not available.issubset(installed):
276
raise ValueError("Available apps isn't a subset of installed "
277
"apps, extra apps: %s" % ", ".join(available - installed))
279
self.stored_app_configs.append(self.app_configs)
280
self.app_configs = OrderedDict(
282
for label, app_config in self.app_configs.items()
283
if app_config.name in available)
286
def unset_available_apps(self):
288
Cancels a previous call to set_available_apps().
290
self.app_configs = self.stored_app_configs.pop()
293
def set_installed_apps(self, installed):
295
Enables a different set of installed apps for get_app_config[s].
297
installed must be an iterable in the same format as INSTALLED_APPS.
299
set_installed_apps() must be balanced with unset_installed_apps(),
300
even if it exits with an exception.
302
Primarily used as a receiver of the setting_changed signal in tests.
304
This method may trigger new imports, which may add new models to the
305
registry of all imported models. They will stay in the registry even
306
after unset_installed_apps(). Since it isn't possible to replay
307
imports safely (eg. that could lead to registering listeners twice),
308
models are registered when they're imported and never removed.
311
raise AppRegistryNotReady("App registry isn't ready yet.")
312
self.stored_app_configs.append(self.app_configs)
313
self.app_configs = OrderedDict()
314
self.apps_ready = self.models_ready = self.ready = False
316
self.populate(installed)
318
def unset_installed_apps(self):
320
Cancels a previous call to set_installed_apps().
322
self.app_configs = self.stored_app_configs.pop()
323
self.apps_ready = self.models_ready = self.ready = True
326
def clear_cache(self):
328
Clears all internal caches, for methods that alter the app registry.
330
This is mostly used in tests.
332
self.get_models.cache_clear()
334
### DEPRECATED METHODS GO BELOW THIS LINE ###
336
def load_app(self, app_name):
338
Loads the app with the provided fully qualified name, and returns the
342
"load_app(app_name) is deprecated.",
343
RemovedInDjango19Warning, stacklevel=2)
344
app_config = AppConfig.create(app_name)
345
app_config.import_models(self.all_models[app_config.label])
346
self.app_configs[app_config.label] = app_config
348
return app_config.models_module
350
def app_cache_ready(self):
352
"app_cache_ready() is deprecated in favor of the ready property.",
353
RemovedInDjango19Warning, stacklevel=2)
356
def get_app(self, app_label):
358
Returns the module containing the models for the given app_label.
361
"get_app_config(app_label).models_module supersedes get_app(app_label).",
362
RemovedInDjango19Warning, stacklevel=2)
364
models_module = self.get_app_config(app_label).models_module
365
except LookupError as exc:
366
# Change the exception type for backwards compatibility.
367
raise ImproperlyConfigured(*exc.args)
368
if models_module is None:
369
raise ImproperlyConfigured(
370
"App '%s' doesn't have a models module." % app_label)
375
Returns a list of all installed modules that contain models.
378
"[a.models_module for a in get_app_configs()] supersedes get_apps().",
379
RemovedInDjango19Warning, stacklevel=2)
380
app_configs = self.get_app_configs()
381
return [app_config.models_module for app_config in app_configs
382
if app_config.models_module is not None]
384
def _get_app_package(self, app):
385
return '.'.join(app.__name__.split('.')[:-1])
387
def get_app_package(self, app_label):
389
"get_app_config(label).name supersedes get_app_package(label).",
390
RemovedInDjango19Warning, stacklevel=2)
391
return self._get_app_package(self.get_app(app_label))
393
def _get_app_path(self, app):
394
if hasattr(app, '__path__'): # models/__init__.py package
395
app_path = app.__path__[0]
396
else: # models.py module
397
app_path = app.__file__
398
return os.path.dirname(upath(app_path))
400
def get_app_path(self, app_label):
402
"get_app_config(label).path supersedes get_app_path(label).",
403
RemovedInDjango19Warning, stacklevel=2)
404
return self._get_app_path(self.get_app(app_label))
406
def get_app_paths(self):
408
Returns a list of paths to all installed apps.
410
Useful for discovering files at conventional locations inside apps
411
(static files, templates, etc.)
414
"[a.path for a in get_app_configs()] supersedes get_app_paths().",
415
RemovedInDjango19Warning, stacklevel=2)
416
self.check_apps_ready()
418
for app in self.get_apps():
419
app_paths.append(self._get_app_path(app))
422
def register_models(self, app_label, *models):
424
Register a set of models as belonging to an app.
427
"register_models(app_label, *models) is deprecated.",
428
RemovedInDjango19Warning, stacklevel=2)
430
self.register_model(app_label, model)
433
apps = Apps(installed_apps=None)