summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAymeric Augustin <aymeric.augustin@m4x.org>2016-09-30 21:08:35 +0200
committerTim Graham <timograham@gmail.com>2016-10-28 18:42:29 -0400
commitefcb7e1ebf2b852a442d00a31634438359eb4039 (patch)
treecce88cc9cb2093fca4fe9a764bcce803d2be7d81
parent20be1918e77414837178d6bf1657068c8306d50c (diff)
Modified readiness check in AppConfig.get_model(s).
It was inconsistent with the equivalent check in Apps.get_model(s) because I made incorrect assumptions when I wrote that code and needlessly complicated readiness checks. This is a backwards-incompatible change.
-rw-r--r--django/apps/config.py18
-rw-r--r--django/apps/registry.py1
-rw-r--r--django/db/migrations/state.py8
-rw-r--r--docs/ref/applications.txt9
-rw-r--r--docs/releases/1.11.txt7
5 files changed, 30 insertions, 13 deletions
diff --git a/django/apps/config.py b/django/apps/config.py
index 88e52c1f05..178fe97130 100644
--- a/django/apps/config.py
+++ b/django/apps/config.py
@@ -1,7 +1,7 @@
import os
from importlib import import_module
-from django.core.exceptions import AppRegistryNotReady, ImproperlyConfigured
+from django.core.exceptions import ImproperlyConfigured
from django.utils._os import upath
from django.utils.module_loading import module_has_submodule
@@ -21,6 +21,10 @@ class AppConfig(object):
# from 'django/contrib/admin/__init__.pyc'>.
self.module = app_module
+ # Reference to the Apps registry that holds this AppConfig. Set by the
+ # registry when it registers the AppConfig instance.
+ self.apps = None
+
# The following attributes could be defined at the class level in a
# subclass, hence the test-and-set pattern.
@@ -151,21 +155,13 @@ class AppConfig(object):
# Entry is a path to an app config class.
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):
"""
Returns the model with the given case-insensitive model_name.
Raises LookupError if no model exists with this name.
"""
- self.check_models_ready()
+ self.apps.check_models_ready()
try:
return self.models[model_name.lower()]
except KeyError:
@@ -186,7 +182,7 @@ class AppConfig(object):
Set the corresponding keyword argument to True to include such models.
Keyword arguments aren't documented; they're a private API.
"""
- self.check_models_ready()
+ self.apps.check_models_ready()
for model in self.models.values():
if model._meta.auto_created and not include_auto_created:
continue
diff --git a/django/apps/registry.py b/django/apps/registry.py
index 8a45830ccf..91d6d5ccef 100644
--- a/django/apps/registry.py
+++ b/django/apps/registry.py
@@ -89,6 +89,7 @@ class Apps(object):
"duplicates: %s" % app_config.label)
self.app_configs[app_config.label] = app_config
+ app_config.apps = self
# Check for duplicate app names.
counts = Counter(
diff --git a/django/db/migrations/state.py b/django/db/migrations/state.py
index 9cbf5659d4..60d192443b 100644
--- a/django/db/migrations/state.py
+++ b/django/db/migrations/state.py
@@ -239,6 +239,10 @@ class StateApps(Apps):
app_configs = [AppConfigStub(label) for label in sorted(real_apps + list(app_labels))]
super(StateApps, self).__init__(app_configs)
+ # The lock gets in the way of copying as implemented in clone(), which
+ # is called whenever Django duplicates a StateApps before updating it.
+ self._lock = None
+
self.render_multiple(list(models.values()) + self.real_models)
# There shouldn't be any operations pending at this point.
@@ -293,6 +297,9 @@ class StateApps(Apps):
clone = StateApps([], {})
clone.all_models = copy.deepcopy(self.all_models)
clone.app_configs = copy.deepcopy(self.app_configs)
+ # Set the pointer to the correct app registry.
+ for app_config in clone.app_configs.values():
+ app_config.apps = clone
# No need to actually clone them, they'll never change
clone.real_models = self.real_models
return clone
@@ -301,6 +308,7 @@ class StateApps(Apps):
self.all_models[app_label][model._meta.model_name] = model
if app_label not in self.app_configs:
self.app_configs[app_label] = AppConfigStub(app_label)
+ self.app_configs[app_label].apps = self
self.app_configs[app_label].models = OrderedDict()
self.app_configs[app_label].models[model._meta.model_name] = model
self.do_pending_operations(model)
diff --git a/docs/ref/applications.txt b/docs/ref/applications.txt
index c289afcef9..110e5e867d 100644
--- a/docs/ref/applications.txt
+++ b/docs/ref/applications.txt
@@ -227,11 +227,16 @@ Methods
Returns an iterable of :class:`~django.db.models.Model` classes for this
application.
+ Requires the app registry to be fully populated.
+
.. method:: AppConfig.get_model(model_name)
Returns the :class:`~django.db.models.Model` with the given
- ``model_name``. Raises :exc:`LookupError` if no such model exists in this
- application. ``model_name`` is case-insensitive.
+ ``model_name``. ``model_name`` is case-insensitive.
+
+ Raises :exc:`LookupError` if no such model exists in this application.
+
+ Requires the app registry to be fully populated.
.. method:: AppConfig.ready()
diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt
index 27f7481fa6..8fd0795d67 100644
--- a/docs/releases/1.11.txt
+++ b/docs/releases/1.11.txt
@@ -579,6 +579,13 @@ Miscellaneous
* ``ConditionalGetMiddleware`` no longer sets the ``Date`` header as Web
servers set that header.
+* :meth:`~django.apps.AppConfig.get_model` and
+ :meth:`~django.apps.AppConfig.get_models` now raise
+ :exc:`~django.core.exceptions.AppRegistryNotReady` if they're called before
+ models of all applications have been loaded. Previously they only required
+ the target application's models to be loaded and thus could return models
+ without all their relations set up.
+
.. _deprecated-features-1.11:
Features deprecated in 1.11