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:
parent
20be1918e7
commit
efcb7e1ebf
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue