Checked more precisely whether the app registry is ready.
Accounted for the three stages of population: app configs, models, ready() methods of app configs.
This commit is contained in:
parent
b48c2c5925
commit
a764a9ccff
|
@ -1,7 +1,7 @@
|
||||||
from importlib import import_module
|
from importlib import import_module
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import AppRegistryNotReady, ImproperlyConfigured
|
||||||
from django.utils.module_loading import module_has_submodule
|
from django.utils.module_loading import module_has_submodule
|
||||||
from django.utils._os import upath
|
from django.utils._os import upath
|
||||||
|
|
||||||
|
@ -139,15 +139,21 @@ class AppConfig(object):
|
||||||
# Entry is a path to an app config class.
|
# Entry is a path to an app config class.
|
||||||
return cls(app_name, app_module)
|
return cls(app_name, app_module)
|
||||||
|
|
||||||
|
def check_models_ready(self):
|
||||||
|
"""
|
||||||
|
Raises an exception if models haven't been imported yet.
|
||||||
|
"""
|
||||||
|
if self.models is None:
|
||||||
|
raise AppRegistryNotReady(
|
||||||
|
"Models for app '%s' haven't been imported yet." % self.label)
|
||||||
|
|
||||||
def get_model(self, model_name):
|
def get_model(self, model_name):
|
||||||
"""
|
"""
|
||||||
Returns the model with the given case-insensitive model_name.
|
Returns the model with the given case-insensitive model_name.
|
||||||
|
|
||||||
Raises LookupError if no model exists with this name.
|
Raises LookupError if no model exists with this name.
|
||||||
"""
|
"""
|
||||||
if self.models is None:
|
self.check_models_ready()
|
||||||
raise LookupError(
|
|
||||||
"App '%s' doesn't have any models." % self.label)
|
|
||||||
try:
|
try:
|
||||||
return self.models[model_name.lower()]
|
return self.models[model_name.lower()]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -169,6 +175,7 @@ class AppConfig(object):
|
||||||
Set the corresponding keyword argument to True to include such models.
|
Set the corresponding keyword argument to True to include such models.
|
||||||
Keyword arguments aren't documented; they're a private API.
|
Keyword arguments aren't documented; they're a private API.
|
||||||
"""
|
"""
|
||||||
|
self.check_models_ready()
|
||||||
for model in self.models.values():
|
for model in self.models.values():
|
||||||
if model._deferred and not include_deferred:
|
if model._deferred and not include_deferred:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -43,7 +43,7 @@ class Apps(object):
|
||||||
self.stored_app_configs = []
|
self.stored_app_configs = []
|
||||||
|
|
||||||
# Whether the registry is populated.
|
# Whether the registry is populated.
|
||||||
self.ready = False
|
self.apps_ready = self.models_ready = self.ready = False
|
||||||
|
|
||||||
# Lock for thread-safe population.
|
# Lock for thread-safe population.
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
|
@ -100,29 +100,41 @@ class Apps(object):
|
||||||
"Application names aren't unique, "
|
"Application names aren't unique, "
|
||||||
"duplicates: %s" % ", ".join(duplicates))
|
"duplicates: %s" % ", ".join(duplicates))
|
||||||
|
|
||||||
|
self.apps_ready = True
|
||||||
|
|
||||||
# Load models.
|
# Load models.
|
||||||
for app_config in self.app_configs.values():
|
for app_config in self.app_configs.values():
|
||||||
all_models = self.all_models[app_config.label]
|
all_models = self.all_models[app_config.label]
|
||||||
app_config.import_models(all_models)
|
app_config.import_models(all_models)
|
||||||
|
|
||||||
self.clear_cache()
|
self.clear_cache()
|
||||||
self.ready = True
|
|
||||||
|
self.models_ready = True
|
||||||
|
|
||||||
for app_config in self.get_app_configs():
|
for app_config in self.get_app_configs():
|
||||||
app_config.ready()
|
app_config.ready()
|
||||||
|
|
||||||
def check_ready(self):
|
self.ready = True
|
||||||
|
|
||||||
|
def check_apps_ready(self):
|
||||||
"""
|
"""
|
||||||
Raises an exception if the registry isn't ready.
|
Raises an exception if all apps haven't been imported yet.
|
||||||
"""
|
"""
|
||||||
if not self.ready:
|
if not self.apps_ready:
|
||||||
raise AppRegistryNotReady()
|
raise AppRegistryNotReady("Apps aren't loaded yet.")
|
||||||
|
|
||||||
|
def check_models_ready(self):
|
||||||
|
"""
|
||||||
|
Raises an exception if all models haven't been imported yet.
|
||||||
|
"""
|
||||||
|
if not self.models_ready:
|
||||||
|
raise AppRegistryNotReady("Models aren't loaded yet.")
|
||||||
|
|
||||||
def get_app_configs(self):
|
def get_app_configs(self):
|
||||||
"""
|
"""
|
||||||
Imports applications and returns an iterable of app configs.
|
Imports applications and returns an iterable of app configs.
|
||||||
"""
|
"""
|
||||||
self.check_ready()
|
self.check_apps_ready()
|
||||||
return self.app_configs.values()
|
return self.app_configs.values()
|
||||||
|
|
||||||
def get_app_config(self, app_label):
|
def get_app_config(self, app_label):
|
||||||
|
@ -131,7 +143,7 @@ class Apps(object):
|
||||||
|
|
||||||
Raises LookupError if no application exists with this label.
|
Raises LookupError if no application exists with this label.
|
||||||
"""
|
"""
|
||||||
self.check_ready()
|
self.check_apps_ready()
|
||||||
try:
|
try:
|
||||||
return self.app_configs[app_label]
|
return self.app_configs[app_label]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -153,7 +165,7 @@ class Apps(object):
|
||||||
|
|
||||||
Set the corresponding keyword argument to True to include such models.
|
Set the corresponding keyword argument to True to include such models.
|
||||||
"""
|
"""
|
||||||
self.check_ready()
|
self.check_models_ready()
|
||||||
if app_mod:
|
if app_mod:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"The app_mod argument of get_models is deprecated.",
|
"The app_mod argument of get_models is deprecated.",
|
||||||
|
@ -184,7 +196,7 @@ class Apps(object):
|
||||||
model exists with this name in the application. Raises ValueError if
|
model exists with this name in the application. Raises ValueError if
|
||||||
called with a single argument that doesn't contain exactly one dot.
|
called with a single argument that doesn't contain exactly one dot.
|
||||||
"""
|
"""
|
||||||
self.check_ready()
|
self.check_models_ready()
|
||||||
if model_name is None:
|
if model_name is None:
|
||||||
app_label, model_name = app_label.split('.')
|
app_label, model_name = app_label.split('.')
|
||||||
return self.get_app_config(app_label).get_model(model_name.lower())
|
return self.get_app_config(app_label).get_model(model_name.lower())
|
||||||
|
@ -207,10 +219,8 @@ class Apps(object):
|
||||||
Checks whether an application with this name exists in the registry.
|
Checks whether an application with this name exists in the registry.
|
||||||
|
|
||||||
app_name is the full name of the app eg. 'django.contrib.admin'.
|
app_name is the full name of the app eg. 'django.contrib.admin'.
|
||||||
|
|
||||||
It's safe to call this method at import time, even while the registry
|
|
||||||
is being populated. It returns False for apps that aren't loaded yet.
|
|
||||||
"""
|
"""
|
||||||
|
self.check_apps_ready()
|
||||||
return any(ac.name == app_name for ac in self.app_configs.values())
|
return any(ac.name == app_name for ac in self.app_configs.values())
|
||||||
|
|
||||||
def get_containing_app_config(self, object_name):
|
def get_containing_app_config(self, object_name):
|
||||||
|
@ -221,10 +231,10 @@ class Apps(object):
|
||||||
|
|
||||||
Returns the app config for the inner application in case of nesting.
|
Returns the app config for the inner application in case of nesting.
|
||||||
Returns None if the object isn't in any registered app config.
|
Returns None if the object isn't in any registered app config.
|
||||||
|
|
||||||
It's safe to call this method at import time, even while the registry
|
|
||||||
is being populated.
|
|
||||||
"""
|
"""
|
||||||
|
# In Django 1.7 and 1.8, it's allowed to call this method at import
|
||||||
|
# time, even while the registry is being populated. In Django 1.9 and
|
||||||
|
# later, that should be forbidden with `self.check_apps_ready()`.
|
||||||
candidates = []
|
candidates = []
|
||||||
for app_config in self.app_configs.values():
|
for app_config in self.app_configs.values():
|
||||||
if object_name.startswith(app_config.name):
|
if object_name.startswith(app_config.name):
|
||||||
|
@ -297,10 +307,11 @@ class Apps(object):
|
||||||
imports safely (eg. that could lead to registering listeners twice),
|
imports safely (eg. that could lead to registering listeners twice),
|
||||||
models are registered when they're imported and never removed.
|
models are registered when they're imported and never removed.
|
||||||
"""
|
"""
|
||||||
self.check_ready()
|
if not self.ready:
|
||||||
|
raise AppRegistryNotReady("App registry isn't ready yet.")
|
||||||
self.stored_app_configs.append(self.app_configs)
|
self.stored_app_configs.append(self.app_configs)
|
||||||
self.app_configs = OrderedDict()
|
self.app_configs = OrderedDict()
|
||||||
self.ready = False
|
self.apps_ready = self.models_ready = self.ready = False
|
||||||
self.clear_cache()
|
self.clear_cache()
|
||||||
self.populate(installed)
|
self.populate(installed)
|
||||||
|
|
||||||
|
@ -309,7 +320,7 @@ class Apps(object):
|
||||||
Cancels a previous call to set_installed_apps().
|
Cancels a previous call to set_installed_apps().
|
||||||
"""
|
"""
|
||||||
self.app_configs = self.stored_app_configs.pop()
|
self.app_configs = self.stored_app_configs.pop()
|
||||||
self.ready = True
|
self.apps_ready = self.models_ready = self.ready = True
|
||||||
self.clear_cache()
|
self.clear_cache()
|
||||||
|
|
||||||
def clear_cache(self):
|
def clear_cache(self):
|
||||||
|
@ -402,7 +413,7 @@ class Apps(object):
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"[a.path for a in get_app_configs()] supersedes get_app_paths().",
|
"[a.path for a in get_app_configs()] supersedes get_app_paths().",
|
||||||
RemovedInDjango19Warning, stacklevel=2)
|
RemovedInDjango19Warning, stacklevel=2)
|
||||||
self.check_ready()
|
self.check_apps_ready()
|
||||||
app_paths = []
|
app_paths = []
|
||||||
for app in self.get_apps():
|
for app in self.get_apps():
|
||||||
app_paths.append(self._get_app_path(app))
|
app_paths.append(self._get_app_path(app))
|
||||||
|
|
|
@ -301,10 +301,6 @@ Application registry
|
||||||
Checks whether an application with the given name exists in the registry.
|
Checks whether an application with the given name exists in the registry.
|
||||||
``app_name`` is the full name of the app, e.g. ``'django.contrib.admin'``.
|
``app_name`` is the full name of the app, e.g. ``'django.contrib.admin'``.
|
||||||
|
|
||||||
Unlike :meth:`~django.apps.apps.get_app_config`, this method can be called
|
|
||||||
safely at import time. If the registry is still being populated, it may
|
|
||||||
return ``False``, even though the app will become available later.
|
|
||||||
|
|
||||||
.. method:: apps.get_model(app_label, model_name)
|
.. method:: apps.get_model(app_label, model_name)
|
||||||
|
|
||||||
Returns the :class:`~django.db.models.Model` with the given ``app_label``
|
Returns the :class:`~django.db.models.Model` with the given ``app_label``
|
||||||
|
@ -365,6 +361,9 @@ processes all applications in the order of :setting:`INSTALLED_APPS`.
|
||||||
the order of :setting:`INSTALLED_APPS`, it's strongly recommended not
|
the order of :setting:`INSTALLED_APPS`, it's strongly recommended not
|
||||||
import any models at this stage.
|
import any models at this stage.
|
||||||
|
|
||||||
|
Once this stage completes, APIs that operate of application configurations
|
||||||
|
such as :meth:`~apps.get_app_config()` become usable.
|
||||||
|
|
||||||
#. Then Django attempts to import the ``models`` submodule of each application,
|
#. Then Django attempts to import the ``models`` submodule of each application,
|
||||||
if there is one.
|
if there is one.
|
||||||
|
|
||||||
|
@ -372,6 +371,9 @@ processes all applications in the order of :setting:`INSTALLED_APPS`.
|
||||||
``models/__init__.py``. Otherwise, the application registry may not be fully
|
``models/__init__.py``. Otherwise, the application registry may not be fully
|
||||||
populated at this point, which could cause the ORM to malfunction.
|
populated at this point, which could cause the ORM to malfunction.
|
||||||
|
|
||||||
|
Once this stage completes, APIs that operate on models such as
|
||||||
|
:meth:`~apps.get_model()` become usable.
|
||||||
|
|
||||||
#. Finally Django runs the :meth:`~AppConfig.ready()` method of each application
|
#. Finally Django runs the :meth:`~AppConfig.ready()` method of each application
|
||||||
configuration.
|
configuration.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue