|
@@ -14,10 +14,6 @@ from django.utils._os import upath
|
|
|
from .base import AppConfig
|
|
|
|
|
|
|
|
|
-class UnavailableApp(Exception):
|
|
|
- pass
|
|
|
-
|
|
|
-
|
|
|
class AppCache(object):
|
|
|
"""
|
|
|
A cache that stores installed applications and their models. Used to
|
|
@@ -43,9 +39,9 @@ class AppCache(object):
|
|
|
# Mapping of labels to AppConfig instances for installed apps.
|
|
|
self.app_configs = OrderedDict()
|
|
|
|
|
|
- # Set of app names. Allows restricting the set of installed apps.
|
|
|
- # Used by TransactionTestCase.available_apps for performance reasons.
|
|
|
- self.available_apps = None
|
|
|
+ # Stack of app_configs. Used to store the current state in
|
|
|
+ # set_available_apps and set_installed_apps.
|
|
|
+ self.stored_app_configs = []
|
|
|
|
|
|
# Internal flags used when populating the master cache.
|
|
|
self._apps_loaded = not self.master
|
|
@@ -157,8 +153,6 @@ class AppCache(object):
|
|
|
for app_config in self.app_configs.values():
|
|
|
if only_with_models_module and app_config.models_module is None:
|
|
|
continue
|
|
|
- if self.available_apps is not None and app_config.name not in self.available_apps:
|
|
|
- continue
|
|
|
yield app_config
|
|
|
|
|
|
def get_app_config(self, app_label, only_with_models_module=False):
|
|
@@ -167,9 +161,6 @@ class AppCache(object):
|
|
|
|
|
|
Raises LookupError if no application exists with this label.
|
|
|
|
|
|
- Raises UnavailableApp when set_available_apps() disables the
|
|
|
- application with this label.
|
|
|
-
|
|
|
If only_with_models_module in True (non-default), imports models and
|
|
|
considers only applications containing a models module.
|
|
|
"""
|
|
@@ -183,8 +174,6 @@ class AppCache(object):
|
|
|
raise LookupError("No installed app with label %r." % app_label)
|
|
|
if only_with_models_module and app_config.models_module is None:
|
|
|
raise LookupError("App with label %r doesn't have a models module." % app_label)
|
|
|
- if self.available_apps is not None and app_config.name not in self.available_apps:
|
|
|
- raise UnavailableApp("App with label %r isn't available." % app_label)
|
|
|
return app_config
|
|
|
|
|
|
def get_models(self, app_mod=None,
|
|
@@ -216,13 +205,7 @@ class AppCache(object):
|
|
|
cache_key = (app_mod, include_auto_created, include_deferred, only_installed, include_swapped)
|
|
|
model_list = None
|
|
|
try:
|
|
|
- model_list = self._get_models_cache[cache_key]
|
|
|
- if self.available_apps is not None and only_installed:
|
|
|
- model_list = [
|
|
|
- m for m in model_list
|
|
|
- if self.app_configs[m._meta.app_label].name in self.available_apps
|
|
|
- ]
|
|
|
- return model_list
|
|
|
+ return self._get_models_cache[cache_key]
|
|
|
except KeyError:
|
|
|
pass
|
|
|
self.populate_models()
|
|
@@ -249,11 +232,6 @@ class AppCache(object):
|
|
|
(not model._meta.swapped or include_swapped))
|
|
|
)
|
|
|
self._get_models_cache[cache_key] = model_list
|
|
|
- if self.available_apps is not None and only_installed:
|
|
|
- model_list = [
|
|
|
- m for m in model_list
|
|
|
- if self.app_configs[m._meta.app_label].name in self.available_apps
|
|
|
- ]
|
|
|
return model_list
|
|
|
|
|
|
def get_model(self, app_label, model_name, only_installed=True):
|
|
@@ -262,9 +240,6 @@ class AppCache(object):
|
|
|
model_name.
|
|
|
|
|
|
Returns None if no model is found.
|
|
|
-
|
|
|
- Raises UnavailableApp when set_available_apps() in in effect and
|
|
|
- doesn't include app_label.
|
|
|
"""
|
|
|
if not self.master:
|
|
|
only_installed = False
|
|
@@ -273,9 +248,6 @@ class AppCache(object):
|
|
|
app_config = self.app_configs.get(app_label)
|
|
|
if app_config is None:
|
|
|
return None
|
|
|
- if (self.available_apps is not None
|
|
|
- and app_config.name not in self.available_apps):
|
|
|
- raise UnavailableApp("App with label %s isn't available." % app_label)
|
|
|
return self.all_models[app_label].get(model_name.lower())
|
|
|
|
|
|
def register_model(self, app_label, model):
|
|
@@ -326,22 +298,57 @@ class AppCache(object):
|
|
|
available must be an iterable of application names.
|
|
|
|
|
|
Primarily used for performance optimization in TransactionTestCase.
|
|
|
+
|
|
|
+ This method is safe is the sense that it doesn't trigger any imports.
|
|
|
"""
|
|
|
- if self.available_apps is not None:
|
|
|
- raise RuntimeError("set_available_apps() may be called only once "
|
|
|
- "in a row; make sure it's paired with unset_available_apps()")
|
|
|
available = set(available)
|
|
|
installed = set(app_config.name for app_config in self.get_app_configs())
|
|
|
if not available.issubset(installed):
|
|
|
raise ValueError("Available apps isn't a subset of installed "
|
|
|
"apps, extra apps: %s" % ", ".join(available - installed))
|
|
|
- self.available_apps = available
|
|
|
+
|
|
|
+ self.stored_app_configs.append(self.app_configs)
|
|
|
+ self.app_configs = OrderedDict(
|
|
|
+ (label, app_config)
|
|
|
+ for label, app_config in self.app_configs.items()
|
|
|
+ if app_config.name in available)
|
|
|
|
|
|
def unset_available_apps(self):
|
|
|
"""
|
|
|
Cancels a previous call to set_available_apps().
|
|
|
"""
|
|
|
- self.available_apps = None
|
|
|
+ self.app_configs = self.stored_app_configs.pop()
|
|
|
+
|
|
|
+ def set_installed_apps(self, installed):
|
|
|
+ """
|
|
|
+ Enables a different set of installed_apps for get_app_config[s].
|
|
|
+
|
|
|
+ installed must be an iterable in the same format as INSTALLED_APPS.
|
|
|
+
|
|
|
+ Primarily used as a receiver of the setting_changed signal in tests.
|
|
|
+
|
|
|
+ This method may trigger new imports, which may add new models to the
|
|
|
+ registry of all imported models. They will stay in the registry even
|
|
|
+ after unset_installed_apps(). Since it isn't possible to replay
|
|
|
+ imports safely (eg. that could lead to registering listeners twice),
|
|
|
+ models are registered when they're imported and never removed.
|
|
|
+ """
|
|
|
+ self.stored_app_configs.append(self.app_configs)
|
|
|
+ self.app_configs = OrderedDict()
|
|
|
+ try:
|
|
|
+ self._apps_loaded = False
|
|
|
+ self.populate_apps()
|
|
|
+ self._models_loaded = False
|
|
|
+ self.populate_models()
|
|
|
+ except Exception:
|
|
|
+ self.unset_installed_apps()
|
|
|
+ raise
|
|
|
+
|
|
|
+ def unset_installed_apps(self):
|
|
|
+ """
|
|
|
+ Cancels a previous call to set_installed_apps().
|
|
|
+ """
|
|
|
+ self.app_configs = self.stored_app_configs.pop()
|
|
|
|
|
|
### DANGEROUS METHODS ### (only used to preserve existing tests)
|
|
|
|
|
@@ -353,15 +360,11 @@ class AppCache(object):
|
|
|
else:
|
|
|
app_config.import_models(self.all_models[app_config.label])
|
|
|
self.app_configs[app_config.label] = app_config
|
|
|
- if self.available_apps is not None:
|
|
|
- self.available_apps.add(app_config.name)
|
|
|
return app_config
|
|
|
|
|
|
def _end_with_app(self, app_config):
|
|
|
if app_config is not None:
|
|
|
del self.app_configs[app_config.label]
|
|
|
- if self.available_apps is not None:
|
|
|
- self.available_apps.discard(app_config.name)
|
|
|
|
|
|
@contextmanager
|
|
|
def _with_app(self, app_name):
|
|
@@ -420,9 +423,6 @@ class AppCache(object):
|
|
|
def get_app(self, app_label):
|
|
|
"""
|
|
|
Returns the module containing the models for the given app_label.
|
|
|
-
|
|
|
- Raises UnavailableApp when set_available_apps() in in effect and
|
|
|
- doesn't include app_label.
|
|
|
"""
|
|
|
warnings.warn(
|
|
|
"get_app_config(app_label).models_module supersedes get_app(app_label).",
|