mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	[1.7.x] Checked more precisely whether the app registry is ready.
Accounted for the three stages of population: app configs, models,
ready() methods of app configs.
Backport of a764a9cc from master
			
			
This commit is contained in:
		| @@ -1,7 +1,7 @@ | |||||||
| from importlib import import_module | from importlib import import_module | ||||||
| import os | import os | ||||||
|  |  | ||||||
| from django.core.exceptions import ImproperlyConfigured | from django.core.exceptions import AppRegistryNotReady, ImproperlyConfigured | ||||||
| from django.utils.module_loading import module_has_submodule | from django.utils.module_loading import module_has_submodule | ||||||
| from django.utils._os import upath | from django.utils._os import upath | ||||||
|  |  | ||||||
| @@ -139,15 +139,21 @@ 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. | ||||||
|         """ |         """ | ||||||
|         if self.models is None: |         self.check_models_ready() | ||||||
|             raise LookupError( |  | ||||||
|                 "App '%s' doesn't have any models." % self.label) |  | ||||||
|         try: |         try: | ||||||
|             return self.models[model_name.lower()] |             return self.models[model_name.lower()] | ||||||
|         except KeyError: |         except KeyError: | ||||||
| @@ -169,6 +175,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() | ||||||
|         for model in self.models.values(): |         for model in self.models.values(): | ||||||
|             if model._deferred and not include_deferred: |             if model._deferred and not include_deferred: | ||||||
|                 continue |                 continue | ||||||
|   | |||||||
| @@ -43,7 +43,7 @@ class Apps(object): | |||||||
|         self.stored_app_configs = [] |         self.stored_app_configs = [] | ||||||
|  |  | ||||||
|         # Whether the registry is populated. |         # Whether the registry is populated. | ||||||
|         self.ready = False |         self.apps_ready = self.models_ready = self.ready = False | ||||||
|  |  | ||||||
|         # Lock for thread-safe population. |         # Lock for thread-safe population. | ||||||
|         self._lock = threading.Lock() |         self._lock = threading.Lock() | ||||||
| @@ -100,29 +100,41 @@ class Apps(object): | |||||||
|                     "Application names aren't unique, " |                     "Application names aren't unique, " | ||||||
|                     "duplicates: %s" % ", ".join(duplicates)) |                     "duplicates: %s" % ", ".join(duplicates)) | ||||||
|  |  | ||||||
|  |             self.apps_ready = True | ||||||
|  |  | ||||||
|             # Load models. |             # Load models. | ||||||
|             for app_config in self.app_configs.values(): |             for app_config in self.app_configs.values(): | ||||||
|                 all_models = self.all_models[app_config.label] |                 all_models = self.all_models[app_config.label] | ||||||
|                 app_config.import_models(all_models) |                 app_config.import_models(all_models) | ||||||
|  |  | ||||||
|             self.clear_cache() |             self.clear_cache() | ||||||
|             self.ready = True |  | ||||||
|  |             self.models_ready = True | ||||||
|  |  | ||||||
|             for app_config in self.get_app_configs(): |             for app_config in self.get_app_configs(): | ||||||
|                 app_config.ready() |                 app_config.ready() | ||||||
|  |  | ||||||
|     def check_ready(self): |             self.ready = True | ||||||
|  |  | ||||||
|  |     def check_apps_ready(self): | ||||||
|         """ |         """ | ||||||
|         Raises an exception if the registry isn't ready. |         Raises an exception if all apps haven't been imported yet. | ||||||
|         """ |         """ | ||||||
|         if not self.ready: |         if not self.apps_ready: | ||||||
|             raise AppRegistryNotReady() |             raise AppRegistryNotReady("Apps aren't loaded yet.") | ||||||
|  |  | ||||||
|  |     def check_models_ready(self): | ||||||
|  |         """ | ||||||
|  |         Raises an exception if all models haven't been imported yet. | ||||||
|  |         """ | ||||||
|  |         if not self.models_ready: | ||||||
|  |             raise AppRegistryNotReady("Models aren't loaded yet.") | ||||||
|  |  | ||||||
|     def get_app_configs(self): |     def get_app_configs(self): | ||||||
|         """ |         """ | ||||||
|         Imports applications and returns an iterable of app configs. |         Imports applications and returns an iterable of app configs. | ||||||
|         """ |         """ | ||||||
|         self.check_ready() |         self.check_apps_ready() | ||||||
|         return self.app_configs.values() |         return self.app_configs.values() | ||||||
|  |  | ||||||
|     def get_app_config(self, app_label): |     def get_app_config(self, app_label): | ||||||
| @@ -131,7 +143,7 @@ class Apps(object): | |||||||
|  |  | ||||||
|         Raises LookupError if no application exists with this label. |         Raises LookupError if no application exists with this label. | ||||||
|         """ |         """ | ||||||
|         self.check_ready() |         self.check_apps_ready() | ||||||
|         try: |         try: | ||||||
|             return self.app_configs[app_label] |             return self.app_configs[app_label] | ||||||
|         except KeyError: |         except KeyError: | ||||||
| @@ -153,7 +165,7 @@ class Apps(object): | |||||||
|  |  | ||||||
|         Set the corresponding keyword argument to True to include such models. |         Set the corresponding keyword argument to True to include such models. | ||||||
|         """ |         """ | ||||||
|         self.check_ready() |         self.check_models_ready() | ||||||
|         if app_mod: |         if app_mod: | ||||||
|             warnings.warn( |             warnings.warn( | ||||||
|                 "The app_mod argument of get_models is deprecated.", |                 "The app_mod argument of get_models is deprecated.", | ||||||
| @@ -184,7 +196,7 @@ class Apps(object): | |||||||
|         model exists with this name in the application. Raises ValueError if |         model exists with this name in the application. Raises ValueError if | ||||||
|         called with a single argument that doesn't contain exactly one dot. |         called with a single argument that doesn't contain exactly one dot. | ||||||
|         """ |         """ | ||||||
|         self.check_ready() |         self.check_models_ready() | ||||||
|         if model_name is None: |         if model_name is None: | ||||||
|             app_label, model_name = app_label.split('.') |             app_label, model_name = app_label.split('.') | ||||||
|         return self.get_app_config(app_label).get_model(model_name.lower()) |         return self.get_app_config(app_label).get_model(model_name.lower()) | ||||||
| @@ -207,10 +219,8 @@ class Apps(object): | |||||||
|         Checks whether an application with this name exists in the registry. |         Checks whether an application with this name exists in the registry. | ||||||
|  |  | ||||||
|         app_name is the full name of the app eg. 'django.contrib.admin'. |         app_name is the full name of the app eg. 'django.contrib.admin'. | ||||||
|  |  | ||||||
|         It's safe to call this method at import time, even while the registry |  | ||||||
|         is being populated. It returns False for apps that aren't loaded yet. |  | ||||||
|         """ |         """ | ||||||
|  |         self.check_apps_ready() | ||||||
|         return any(ac.name == app_name for ac in self.app_configs.values()) |         return any(ac.name == app_name for ac in self.app_configs.values()) | ||||||
|  |  | ||||||
|     def get_containing_app_config(self, object_name): |     def get_containing_app_config(self, object_name): | ||||||
| @@ -221,10 +231,10 @@ class Apps(object): | |||||||
|  |  | ||||||
|         Returns the app config for the inner application in case of nesting. |         Returns the app config for the inner application in case of nesting. | ||||||
|         Returns None if the object isn't in any registered app config. |         Returns None if the object isn't in any registered app config. | ||||||
|  |  | ||||||
|         It's safe to call this method at import time, even while the registry |  | ||||||
|         is being populated. |  | ||||||
|         """ |         """ | ||||||
|  |         # In Django 1.7 and 1.8, it's allowed to call this method at import | ||||||
|  |         # time, even while the registry is being populated. In Django 1.9 and | ||||||
|  |         # later, that should be forbidden with `self.check_apps_ready()`. | ||||||
|         candidates = [] |         candidates = [] | ||||||
|         for app_config in self.app_configs.values(): |         for app_config in self.app_configs.values(): | ||||||
|             if object_name.startswith(app_config.name): |             if object_name.startswith(app_config.name): | ||||||
| @@ -297,10 +307,11 @@ class Apps(object): | |||||||
|         imports safely (eg. that could lead to registering listeners twice), |         imports safely (eg. that could lead to registering listeners twice), | ||||||
|         models are registered when they're imported and never removed. |         models are registered when they're imported and never removed. | ||||||
|         """ |         """ | ||||||
|         self.check_ready() |         if not self.ready: | ||||||
|  |             raise AppRegistryNotReady("App registry isn't ready yet.") | ||||||
|         self.stored_app_configs.append(self.app_configs) |         self.stored_app_configs.append(self.app_configs) | ||||||
|         self.app_configs = OrderedDict() |         self.app_configs = OrderedDict() | ||||||
|         self.ready = False |         self.apps_ready = self.models_ready = self.ready = False | ||||||
|         self.clear_cache() |         self.clear_cache() | ||||||
|         self.populate(installed) |         self.populate(installed) | ||||||
|  |  | ||||||
| @@ -309,7 +320,7 @@ class Apps(object): | |||||||
|         Cancels a previous call to set_installed_apps(). |         Cancels a previous call to set_installed_apps(). | ||||||
|         """ |         """ | ||||||
|         self.app_configs = self.stored_app_configs.pop() |         self.app_configs = self.stored_app_configs.pop() | ||||||
|         self.ready = True |         self.apps_ready = self.models_ready = self.ready = True | ||||||
|         self.clear_cache() |         self.clear_cache() | ||||||
|  |  | ||||||
|     def clear_cache(self): |     def clear_cache(self): | ||||||
| @@ -402,7 +413,7 @@ class Apps(object): | |||||||
|         warnings.warn( |         warnings.warn( | ||||||
|             "[a.path for a in get_app_configs()] supersedes get_app_paths().", |             "[a.path for a in get_app_configs()] supersedes get_app_paths().", | ||||||
|             RemovedInDjango19Warning, stacklevel=2) |             RemovedInDjango19Warning, stacklevel=2) | ||||||
|         self.check_ready() |         self.check_apps_ready() | ||||||
|         app_paths = [] |         app_paths = [] | ||||||
|         for app in self.get_apps(): |         for app in self.get_apps(): | ||||||
|             app_paths.append(self._get_app_path(app)) |             app_paths.append(self._get_app_path(app)) | ||||||
|   | |||||||
| @@ -301,10 +301,6 @@ Application registry | |||||||
|     Checks whether an application with the given name exists in the registry. |     Checks whether an application with the given name exists in the registry. | ||||||
|     ``app_name`` is the full name of the app, e.g. ``'django.contrib.admin'``. |     ``app_name`` is the full name of the app, e.g. ``'django.contrib.admin'``. | ||||||
|  |  | ||||||
|     Unlike :meth:`~django.apps.apps.get_app_config`, this method can be called |  | ||||||
|     safely at import time. If the registry is still being populated, it may |  | ||||||
|     return ``False``, even though the app will become available later. |  | ||||||
|  |  | ||||||
| .. method:: apps.get_model(app_label, model_name) | .. method:: apps.get_model(app_label, model_name) | ||||||
|  |  | ||||||
|     Returns the :class:`~django.db.models.Model` with the given ``app_label`` |     Returns the :class:`~django.db.models.Model` with the given ``app_label`` | ||||||
| @@ -365,6 +361,9 @@ processes all applications in the order of :setting:`INSTALLED_APPS`. | |||||||
|    the order of :setting:`INSTALLED_APPS`, it's strongly recommended not |    the order of :setting:`INSTALLED_APPS`, it's strongly recommended not | ||||||
|    import any models at this stage. |    import any models at this stage. | ||||||
|  |  | ||||||
|  |    Once this stage completes, APIs that operate of application configurations | ||||||
|  |    such as :meth:`~apps.get_app_config()` become usable. | ||||||
|  |  | ||||||
| #. Then Django attempts to import the ``models`` submodule of each application, | #. Then Django attempts to import the ``models`` submodule of each application, | ||||||
|    if there is one. |    if there is one. | ||||||
|  |  | ||||||
| @@ -372,6 +371,9 @@ processes all applications in the order of :setting:`INSTALLED_APPS`. | |||||||
|    ``models/__init__.py``. Otherwise, the application registry may not be fully |    ``models/__init__.py``. Otherwise, the application registry may not be fully | ||||||
|    populated at this point, which could cause the ORM to malfunction. |    populated at this point, which could cause the ORM to malfunction. | ||||||
|  |  | ||||||
|  |    Once this stage completes, APIs that operate on models such as | ||||||
|  |    :meth:`~apps.get_model()` become usable. | ||||||
|  |  | ||||||
| #. Finally Django runs the :meth:`~AppConfig.ready()` method of each application | #. Finally Django runs the :meth:`~AppConfig.ready()` method of each application | ||||||
|    configuration. |    configuration. | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user