mirror of https://github.com/django/django.git
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
|
import os
|
||||||
from importlib import import_module
|
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._os import upath
|
||||||
from django.utils.module_loading import module_has_submodule
|
from django.utils.module_loading import module_has_submodule
|
||||||
|
|
||||||
|
@ -21,6 +21,10 @@ class AppConfig(object):
|
||||||
# from 'django/contrib/admin/__init__.pyc'>.
|
# from 'django/contrib/admin/__init__.pyc'>.
|
||||||
self.module = app_module
|
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
|
# The following attributes could be defined at the class level in a
|
||||||
# subclass, hence the test-and-set pattern.
|
# subclass, hence the test-and-set pattern.
|
||||||
|
|
||||||
|
@ -151,21 +155,13 @@ class AppConfig(object):
|
||||||
# Entry is a path to an app config class.
|
# Entry is a path to an app config class.
|
||||||
return cls(app_name, app_module)
|
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):
|
def get_model(self, model_name):
|
||||||
"""
|
"""
|
||||||
Returns the model with the given case-insensitive model_name.
|
Returns the model with the given case-insensitive model_name.
|
||||||
|
|
||||||
Raises LookupError if no model exists with this name.
|
Raises LookupError if no model exists with this name.
|
||||||
"""
|
"""
|
||||||
self.check_models_ready()
|
self.apps.check_models_ready()
|
||||||
try:
|
try:
|
||||||
return self.models[model_name.lower()]
|
return self.models[model_name.lower()]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -186,7 +182,7 @@ class AppConfig(object):
|
||||||
Set the corresponding keyword argument to True to include such models.
|
Set the corresponding keyword argument to True to include such models.
|
||||||
Keyword arguments aren't documented; they're a private API.
|
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():
|
for model in self.models.values():
|
||||||
if model._meta.auto_created and not include_auto_created:
|
if model._meta.auto_created and not include_auto_created:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -89,6 +89,7 @@ class Apps(object):
|
||||||
"duplicates: %s" % app_config.label)
|
"duplicates: %s" % app_config.label)
|
||||||
|
|
||||||
self.app_configs[app_config.label] = app_config
|
self.app_configs[app_config.label] = app_config
|
||||||
|
app_config.apps = self
|
||||||
|
|
||||||
# Check for duplicate app names.
|
# Check for duplicate app names.
|
||||||
counts = Counter(
|
counts = Counter(
|
||||||
|
|
|
@ -239,6 +239,10 @@ class StateApps(Apps):
|
||||||
app_configs = [AppConfigStub(label) for label in sorted(real_apps + list(app_labels))]
|
app_configs = [AppConfigStub(label) for label in sorted(real_apps + list(app_labels))]
|
||||||
super(StateApps, self).__init__(app_configs)
|
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)
|
self.render_multiple(list(models.values()) + self.real_models)
|
||||||
|
|
||||||
# There shouldn't be any operations pending at this point.
|
# There shouldn't be any operations pending at this point.
|
||||||
|
@ -293,6 +297,9 @@ class StateApps(Apps):
|
||||||
clone = StateApps([], {})
|
clone = StateApps([], {})
|
||||||
clone.all_models = copy.deepcopy(self.all_models)
|
clone.all_models = copy.deepcopy(self.all_models)
|
||||||
clone.app_configs = copy.deepcopy(self.app_configs)
|
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
|
# No need to actually clone them, they'll never change
|
||||||
clone.real_models = self.real_models
|
clone.real_models = self.real_models
|
||||||
return clone
|
return clone
|
||||||
|
@ -301,6 +308,7 @@ class StateApps(Apps):
|
||||||
self.all_models[app_label][model._meta.model_name] = model
|
self.all_models[app_label][model._meta.model_name] = model
|
||||||
if app_label not in self.app_configs:
|
if app_label not in self.app_configs:
|
||||||
self.app_configs[app_label] = AppConfigStub(app_label)
|
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 = OrderedDict()
|
||||||
self.app_configs[app_label].models[model._meta.model_name] = model
|
self.app_configs[app_label].models[model._meta.model_name] = model
|
||||||
self.do_pending_operations(model)
|
self.do_pending_operations(model)
|
||||||
|
|
|
@ -227,11 +227,16 @@ Methods
|
||||||
Returns an iterable of :class:`~django.db.models.Model` classes for this
|
Returns an iterable of :class:`~django.db.models.Model` classes for this
|
||||||
application.
|
application.
|
||||||
|
|
||||||
|
Requires the app registry to be fully populated.
|
||||||
|
|
||||||
.. method:: AppConfig.get_model(model_name)
|
.. method:: AppConfig.get_model(model_name)
|
||||||
|
|
||||||
Returns the :class:`~django.db.models.Model` with the given
|
Returns the :class:`~django.db.models.Model` with the given
|
||||||
``model_name``. Raises :exc:`LookupError` if no such model exists in this
|
``model_name``. ``model_name`` is case-insensitive.
|
||||||
application. ``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()
|
.. method:: AppConfig.ready()
|
||||||
|
|
||||||
|
|
|
@ -579,6 +579,13 @@ Miscellaneous
|
||||||
* ``ConditionalGetMiddleware`` no longer sets the ``Date`` header as Web
|
* ``ConditionalGetMiddleware`` no longer sets the ``Date`` header as Web
|
||||||
servers set that header.
|
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:
|
.. _deprecated-features-1.11:
|
||||||
|
|
||||||
Features deprecated in 1.11
|
Features deprecated in 1.11
|
||||||
|
|
Loading…
Reference in New Issue