Merged Apps.populate_apps() and populate_models().

After the recent series of refactorings, there's no reason to keep
two distinct methods.

Refs #21681.
This commit is contained in:
Aymeric Augustin 2013-12-30 23:15:29 +01:00
parent 966de84973
commit 1c242a297b
4 changed files with 22 additions and 65 deletions

View File

@ -13,5 +13,4 @@ def setup():
# INSTALLED_APPS or any other setting) and populate the app registry. # INSTALLED_APPS or any other setting) and populate the app registry.
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
apps.populate_apps(settings.INSTALLED_APPS) apps.populate(settings.INSTALLED_APPS)
apps.populate_models()

View File

@ -152,8 +152,7 @@ class AppConfig(object):
# Dictionary of models for this app, primarily maintained in the # Dictionary of models for this app, primarily maintained in the
# 'all_models' attribute of the Apps this AppConfig is attached to. # 'all_models' attribute of the Apps this AppConfig is attached to.
# Injected as a parameter because it gets populated when models are # Injected as a parameter because it gets populated when models are
# imported, which might happen before populate_models() runs (or at # imported, which might happen before populate() imports models.
# least used to).
self.models = all_models self.models = all_models
if module_has_submodule(self.module, MODELS_MODULE_NAME): if module_has_submodule(self.module, MODELS_MODULE_NAME):

View File

@ -41,43 +41,39 @@ class Apps(object):
# set_available_apps and set_installed_apps. # set_available_apps and set_installed_apps.
self.stored_app_configs = [] self.stored_app_configs = []
# Internal flags used when populating the registry. # Whether the registry is populated.
self._apps_loaded = False self.ready = False
self._models_loaded = False
# Pending lookups for lazy relations. # Pending lookups for lazy relations.
self._pending_lookups = {} self._pending_lookups = {}
# Populate apps and models, unless it's the master registry. # Populate apps and models, unless it's the master registry.
if installed_apps is not None: if installed_apps is not None:
self.populate_apps(installed_apps) self.populate(installed_apps)
self.populate_models()
def populate_apps(self, installed_apps=None): def populate(self, installed_apps=None):
""" """
Populate app-related information. Loads application configurations and models.
This method imports each application module. This method imports each application module and then each model module.
It is thread safe and idempotent, but not reentrant. It is thread safe and idempotent, but not reentrant.
""" """
if self._apps_loaded: if self.ready:
return return
# Since populate_apps() may be a side effect of imports, and since # Since populate() may be a side effect of imports, and since it will
# it will itself import modules, an ABBA deadlock between threads # itself import modules, an ABBA deadlock between threads would be
# would be possible if we didn't take the import lock. See #18251. # possible if we didn't take the import lock. See #18251.
with import_lock(): with import_lock():
if self._apps_loaded: if self.ready:
return return
# app_config should be pristine, otherwise the code below won't # app_config should be pristine, otherwise the code below won't
# guarantee that the order matches the order in INSTALLED_APPS. # guarantee that the order matches the order in INSTALLED_APPS.
if self.app_configs: if self.app_configs:
raise RuntimeError("populate_apps() isn't reentrant") raise RuntimeError("populate() isn't reentrant")
# Application modules aren't expected to import anything, and # Load app configs and app modules.
# especially not other application modules, even indirectly.
# Therefore we simply import them sequentially.
for entry in installed_apps: for entry in installed_apps:
if isinstance(entry, AppConfig): if isinstance(entry, AppConfig):
app_config = entry app_config = entry
@ -85,36 +81,13 @@ class Apps(object):
app_config = AppConfig.create(entry) app_config = AppConfig.create(entry)
self.app_configs[app_config.label] = app_config self.app_configs[app_config.label] = app_config
self.clear_cache() # Load models.
self._apps_loaded = True
def populate_models(self):
"""
Populate model-related information.
This method imports each models module.
It is thread safe, idempotent and reentrant.
"""
if self._models_loaded:
return
# Since populate_models() may be a side effect of imports, and since
# it will itself import modules, an ABBA deadlock between threads
# would be possible if we didn't take the import lock. See #18251.
with import_lock():
if self._models_loaded:
return
if not self._apps_loaded:
raise RuntimeError(
"populate_models() must run after populate_apps()")
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._models_loaded = True self.ready = True
for app_config in self.get_app_configs(): for app_config in self.get_app_configs():
app_config.setup() app_config.setup()
@ -123,21 +96,11 @@ class Apps(object):
""" """
Raises an exception if the registry isn't ready. Raises an exception if the registry isn't ready.
""" """
if not self._models_loaded: if not self.ready:
raise RuntimeError( raise RuntimeError(
"App registry isn't populated yet. " "App registry isn't populated yet. "
"Have you called django.setup()?") "Have you called django.setup()?")
@property
def ready(self):
"""
Whether the registry is fully populated.
Useful for code that wants to cache the results of get_models() for
themselves once it is safe to do so.
"""
return self._models_loaded # implies self._apps_loaded.
def get_app_configs(self, only_with_models_module=False): def get_app_configs(self, only_with_models_module=False):
""" """
Imports applications and returns an iterable of app configs. Imports applications and returns an iterable of app configs.
@ -306,19 +269,16 @@ class Apps(object):
self.check_ready() self.check_ready()
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.clear_cache() self.clear_cache()
self._apps_loaded = False self.populate(installed)
self.populate_apps(installed)
self._models_loaded = False
self.populate_models()
def unset_installed_apps(self): def unset_installed_apps(self):
""" """
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._apps_loaded = True self.ready = True
self._models_loaded = True
self.clear_cache() self.clear_cache()
def clear_cache(self): def clear_cache(self):

View File

@ -121,8 +121,7 @@ def get_commands():
# avoid catching ImproperlyConfigured errors that aren't caused # avoid catching ImproperlyConfigured errors that aren't caused
# by the absence of a settings module. # by the absence of a settings module.
from django.apps import apps from django.apps import apps
apps.populate_apps(installed_apps) apps.populate(installed_apps)
apps.populate_models()
app_configs = apps.get_app_configs() app_configs = apps.get_app_configs()
app_names = [app_config.name for app_config in app_configs] app_names = [app_config.name for app_config in app_configs]