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.
This commit is contained in:
Aymeric Augustin 2016-09-30 21:08:35 +02:00 committed by Tim Graham
parent 20be1918e7
commit efcb7e1ebf
5 changed files with 30 additions and 13 deletions

View File

@ -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

View File

@ -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(

View File

@ -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)

View File

@ -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()

View File

@ -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