mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	Stopped populating the app registry as a side effect.
Since it triggers imports, it shouldn't be done lightly. This commit adds a public API for doing it explicitly, django.setup(), and does it automatically when using manage.py and wsgi.py.
This commit is contained in:
		| @@ -6,3 +6,12 @@ def get_version(*args, **kwargs): | |||||||
|     # Only import if it's actually called. |     # Only import if it's actually called. | ||||||
|     from django.utils.version import get_version |     from django.utils.version import get_version | ||||||
|     return get_version(*args, **kwargs) |     return get_version(*args, **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def setup(): | ||||||
|  |     # Configure the settings (this happens as a side effect of accessing | ||||||
|  |     # INSTALLED_APPS or any other setting) and populate the app registry. | ||||||
|  |     from django.apps import apps | ||||||
|  |     from django.conf import settings | ||||||
|  |     apps.populate_apps(settings.INSTALLED_APPS) | ||||||
|  |     apps.populate_models() | ||||||
|   | |||||||
| @@ -114,8 +114,6 @@ class AppConfig(object): | |||||||
|         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. | ||||||
|  |  | ||||||
|         This method assumes that apps.populate_models() has run. |  | ||||||
|         """ |         """ | ||||||
|         if self.models is None: |         if self.models is None: | ||||||
|             raise LookupError( |             raise LookupError( | ||||||
| @@ -140,8 +138,6 @@ 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. | ||||||
|  |  | ||||||
|         This method assumes that apps.populate_models() has run. |  | ||||||
|         """ |         """ | ||||||
|         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: | ||||||
| @@ -156,7 +152,8 @@ class AppConfig(object): | |||||||
|         # Dictionary of models for this app, primarily maintained in the |         # Dictionary of models for this app, primarily maintained in the | ||||||
|         # 'all_models' attribute of the Apps this AppConfig is attached to. |         # 'all_models' attribute of the Apps this AppConfig is attached to. | ||||||
|         # Injected as a parameter because it gets populated when models are |         # Injected as a parameter because it gets populated when models are | ||||||
|         # imported, which may happen before populate_models() runs. |         # imported, which might happen before populate_models() runs (or at | ||||||
|  |         # least used to). | ||||||
|         self.models = all_models |         self.models = all_models | ||||||
|  |  | ||||||
|         if module_has_submodule(self.module, MODELS_MODULE_NAME): |         if module_has_submodule(self.module, MODELS_MODULE_NAME): | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ import os | |||||||
| import sys | import sys | ||||||
| import warnings | import warnings | ||||||
|  |  | ||||||
| from django.conf import settings |  | ||||||
| from django.core.exceptions import ImproperlyConfigured | from django.core.exceptions import ImproperlyConfigured | ||||||
| from django.utils import lru_cache | from django.utils import lru_cache | ||||||
| from django.utils.module_loading import import_lock | from django.utils.module_loading import import_lock | ||||||
| @@ -79,8 +78,6 @@ class Apps(object): | |||||||
|             # Application modules aren't expected to import anything, and |             # Application modules aren't expected to import anything, and | ||||||
|             # especially not other application modules, even indirectly. |             # especially not other application modules, even indirectly. | ||||||
|             # Therefore we simply import them sequentially. |             # Therefore we simply import them sequentially. | ||||||
|             if installed_apps is None: |  | ||||||
|                 installed_apps = settings.INSTALLED_APPS |  | ||||||
|             for entry in installed_apps: |             for entry in installed_apps: | ||||||
|                 if isinstance(entry, AppConfig): |                 if isinstance(entry, AppConfig): | ||||||
|                     app_config = entry |                     app_config = entry | ||||||
| @@ -108,7 +105,9 @@ class Apps(object): | |||||||
|             if self._models_loaded: |             if self._models_loaded: | ||||||
|                 return |                 return | ||||||
|  |  | ||||||
|             self.populate_apps() |             if not self._apps_loaded: | ||||||
|  |                 raise RuntimeError( | ||||||
|  |                     "populate_models() must run after populate_apps()") | ||||||
|  |  | ||||||
|             # Models modules are likely to import other models modules, for |             # Models modules are likely to import other models modules, for | ||||||
|             # example to reference related objects. As a consequence: |             # example to reference related objects. As a consequence: | ||||||
| @@ -144,6 +143,15 @@ class Apps(object): | |||||||
|                 for app_config in self.get_app_configs(): |                 for app_config in self.get_app_configs(): | ||||||
|                     app_config.setup() |                     app_config.setup() | ||||||
|  |  | ||||||
|  |     def check_ready(self): | ||||||
|  |         """ | ||||||
|  |         Raises an exception if the registry isn't ready. | ||||||
|  |         """ | ||||||
|  |         if not self._models_loaded: | ||||||
|  |             raise RuntimeError( | ||||||
|  |                 "App registry isn't populated yet. " | ||||||
|  |                 "Have you called django.setup()?") | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def ready(self): |     def ready(self): | ||||||
|         """ |         """ | ||||||
| @@ -161,11 +169,7 @@ class Apps(object): | |||||||
|         If only_with_models_module in True (non-default), imports models and |         If only_with_models_module in True (non-default), imports models and | ||||||
|         considers only applications containing a models module. |         considers only applications containing a models module. | ||||||
|         """ |         """ | ||||||
|         if only_with_models_module: |         self.check_ready() | ||||||
|             self.populate_models() |  | ||||||
|         else: |  | ||||||
|             self.populate_apps() |  | ||||||
|  |  | ||||||
|         for app_config in self.app_configs.values(): |         for app_config in self.app_configs.values(): | ||||||
|             if only_with_models_module and app_config.models_module is None: |             if only_with_models_module and app_config.models_module is None: | ||||||
|                 continue |                 continue | ||||||
| @@ -180,11 +184,7 @@ class Apps(object): | |||||||
|         If only_with_models_module in True (non-default), imports models and |         If only_with_models_module in True (non-default), imports models and | ||||||
|         considers only applications containing a models module. |         considers only applications containing a models module. | ||||||
|         """ |         """ | ||||||
|         if only_with_models_module: |         self.check_ready() | ||||||
|             self.populate_models() |  | ||||||
|         else: |  | ||||||
|             self.populate_apps() |  | ||||||
|  |  | ||||||
|         app_config = self.app_configs.get(app_label) |         app_config = self.app_configs.get(app_label) | ||||||
|         if app_config is None: |         if app_config is None: | ||||||
|             raise LookupError("No installed app with label '%s'." % app_label) |             raise LookupError("No installed app with label '%s'." % app_label) | ||||||
| @@ -208,8 +208,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.populate_models() |         self.check_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.", | ||||||
| @@ -236,7 +235,7 @@ class Apps(object): | |||||||
|         Raises LookupError if no application exists with this label, or no |         Raises LookupError if no application exists with this label, or no | ||||||
|         model exists with this name in the application. |         model exists with this name in the application. | ||||||
|         """ |         """ | ||||||
|         self.populate_models() |         self.check_ready() | ||||||
|         return self.get_app_config(app_label).get_model(model_name.lower()) |         return self.get_app_config(app_label).get_model(model_name.lower()) | ||||||
|  |  | ||||||
|     def register_model(self, app_label, model): |     def register_model(self, app_label, model): | ||||||
| @@ -328,7 +327,8 @@ 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.stored_app_configs.append((self.app_configs, self._apps_loaded, self._models_loaded)) |         self.check_ready() | ||||||
|  |         self.stored_app_configs.append(self.app_configs) | ||||||
|         self.app_configs = OrderedDict() |         self.app_configs = OrderedDict() | ||||||
|         self.clear_cache() |         self.clear_cache() | ||||||
|         self._apps_loaded = False |         self._apps_loaded = False | ||||||
| @@ -340,7 +340,9 @@ class Apps(object): | |||||||
|         """ |         """ | ||||||
|         Cancels a previous call to set_installed_apps(). |         Cancels a previous call to set_installed_apps(). | ||||||
|         """ |         """ | ||||||
|         self.app_configs, self._apps_loaded, self._models_loaded = self.stored_app_configs.pop() |         self.app_configs = self.stored_app_configs.pop() | ||||||
|  |         self._apps_loaded = True | ||||||
|  |         self._models_loaded = True | ||||||
|         self.clear_cache() |         self.clear_cache() | ||||||
|  |  | ||||||
|     def clear_cache(self): |     def clear_cache(self): | ||||||
| @@ -429,9 +431,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().", | ||||||
|             PendingDeprecationWarning, stacklevel=2) |             PendingDeprecationWarning, stacklevel=2) | ||||||
|  |         self.check_ready() | ||||||
|         self.populate_models() |  | ||||||
|  |  | ||||||
|         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)) | ||||||
|   | |||||||
| @@ -160,7 +160,6 @@ class AdminSite(object): | |||||||
|         The default implementation checks that admin and contenttypes apps are |         The default implementation checks that admin and contenttypes apps are | ||||||
|         installed, as well as the auth context processor. |         installed, as well as the auth context processor. | ||||||
|         """ |         """ | ||||||
|         apps.populate_apps() |  | ||||||
|         if not apps.has_app('django.contrib.admin'): |         if not apps.has_app('django.contrib.admin'): | ||||||
|             raise ImproperlyConfigured("Put 'django.contrib.admin' in your " |             raise ImproperlyConfigured("Put 'django.contrib.admin' in your " | ||||||
|                 "INSTALLED_APPS setting in order to use the admin application.") |                 "INSTALLED_APPS setting in order to use the admin application.") | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
| from django.apps import apps |  | ||||||
| from django.core.exceptions import ImproperlyConfigured | from django.core.exceptions import ImproperlyConfigured | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.db.models.fields import FieldDoesNotExist | from django.db.models.fields import FieldDoesNotExist | ||||||
| @@ -15,10 +14,6 @@ __all__ = ['BaseValidator', 'InlineValidator'] | |||||||
|  |  | ||||||
|  |  | ||||||
| class BaseValidator(object): | class BaseValidator(object): | ||||||
|     def __init__(self): |  | ||||||
|         # Before we can introspect models, they need the app registry to be |  | ||||||
|         # fully loaded so that inter-relations are set up correctly. |  | ||||||
|         apps.populate_models() |  | ||||||
|  |  | ||||||
|     def validate(self, cls, model): |     def validate(self, cls, model): | ||||||
|         for m in dir(self): |         for m in dir(self): | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ Module for abstract serializer/unserializer base classes. | |||||||
| """ | """ | ||||||
| import warnings | import warnings | ||||||
|  |  | ||||||
| from django.apps import apps |  | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.utils import six | from django.utils import six | ||||||
|  |  | ||||||
| @@ -137,9 +136,6 @@ class Deserializer(six.Iterator): | |||||||
|             self.stream = six.StringIO(stream_or_string) |             self.stream = six.StringIO(stream_or_string) | ||||||
|         else: |         else: | ||||||
|             self.stream = stream_or_string |             self.stream = stream_or_string | ||||||
|         # Make sure the app registy is loaded before deserialization starts |  | ||||||
|         # (otherwise subclass calls to get_model() and friends might fail...) |  | ||||||
|         apps.populate_models() |  | ||||||
|  |  | ||||||
|     def __iter__(self): |     def __iter__(self): | ||||||
|         return self |         return self | ||||||
|   | |||||||
| @@ -88,8 +88,6 @@ def Deserializer(object_list, **options): | |||||||
|     db = options.pop('using', DEFAULT_DB_ALIAS) |     db = options.pop('using', DEFAULT_DB_ALIAS) | ||||||
|     ignore = options.pop('ignorenonexistent', False) |     ignore = options.pop('ignorenonexistent', False) | ||||||
|  |  | ||||||
|     apps.populate_models() |  | ||||||
|  |  | ||||||
|     for d in object_list: |     for d in object_list: | ||||||
|         # Look up the model and starting build a dict of data for it. |         # Look up the model and starting build a dict of data for it. | ||||||
|         Model = _get_model(d["model"]) |         Model = _get_model(d["model"]) | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| from django.apps import apps | import django | ||||||
| from django.conf import settings |  | ||||||
| from django.core.handlers.wsgi import WSGIHandler | from django.core.handlers.wsgi import WSGIHandler | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -12,9 +11,5 @@ def get_wsgi_application(): | |||||||
|     case the internal WSGI implementation changes or moves in the future. |     case the internal WSGI implementation changes or moves in the future. | ||||||
|  |  | ||||||
|     """ |     """ | ||||||
|     # Configure the settings (this happens automatically on the first access). |     django.setup() | ||||||
|     # Populate the app registry. |  | ||||||
|     apps.populate_apps(settings.INSTALLED_APPS) |  | ||||||
|     apps.populate_models() |  | ||||||
|  |  | ||||||
|     return WSGIHandler() |     return WSGIHandler() | ||||||
|   | |||||||
| @@ -30,6 +30,5 @@ def get_app_errors(): | |||||||
|     try: |     try: | ||||||
|         return apps.app_errors |         return apps.app_errors | ||||||
|     except AttributeError: |     except AttributeError: | ||||||
|         apps.populate_models() |  | ||||||
|         apps.app_errors = {} |         apps.app_errors = {} | ||||||
|         return apps.app_errors |         return apps.app_errors | ||||||
|   | |||||||
| @@ -602,9 +602,19 @@ the Python import path to your :file:`mysite/settings.py` file. | |||||||
| .. admonition:: Bypassing manage.py | .. admonition:: Bypassing manage.py | ||||||
|  |  | ||||||
|     If you'd rather not use :file:`manage.py`, no problem. Just set the |     If you'd rather not use :file:`manage.py`, no problem. Just set the | ||||||
|     ``DJANGO_SETTINGS_MODULE`` environment variable to ``mysite.settings`` and |     :envvar:`DJANGO_SETTINGS_MODULE` environment variable to | ||||||
|     run ``python`` from the same directory :file:`manage.py` is in (or ensure |     ``mysite.settings``, start a plain Python shell, and set up Django:: | ||||||
|     that directory is on the Python path, so that ``import mysite`` works). |  | ||||||
|  |     >>> import django | ||||||
|  |     >>> django.setup() | ||||||
|  |  | ||||||
|  |     If this raises an :exc:`~exceptions.AttributeError`, you're probably using | ||||||
|  |     a version of Django that doesn't match this tutorial version. You'll want | ||||||
|  |     to either switch to the older tutorial or the newer Django version. | ||||||
|  |  | ||||||
|  |     You must run ``python`` from the same directory :file:`manage.py` is in, | ||||||
|  |     or ensure that directory is on the Python path, so that ``import mysite`` | ||||||
|  |     works. | ||||||
|  |  | ||||||
|     For more information on all of this, see the :doc:`django-admin.py |     For more information on all of this, see the :doc:`django-admin.py | ||||||
|     documentation </ref/django-admin>`. |     documentation </ref/django-admin>`. | ||||||
|   | |||||||
| @@ -14,6 +14,12 @@ two things for you before delegating to ``django-admin.py``: | |||||||
| * It sets the :envvar:`DJANGO_SETTINGS_MODULE` environment variable so that | * It sets the :envvar:`DJANGO_SETTINGS_MODULE` environment variable so that | ||||||
|   it points to your project's ``settings.py`` file. |   it points to your project's ``settings.py`` file. | ||||||
|  |  | ||||||
|  | * It calls ``django.setup()`` to initialize various internals of Django. | ||||||
|  |  | ||||||
|  | .. versionadded:: 1.7 | ||||||
|  |  | ||||||
|  |     ``django.setup()`` didn't exist in previous versions of Django. | ||||||
|  |  | ||||||
| The ``django-admin.py`` script should be on your system path if you installed | The ``django-admin.py`` script should be on your system path if you installed | ||||||
| Django via its ``setup.py`` utility. If it's not on your path, you can find it | Django via its ``setup.py`` utility. If it's not on your path, you can find it | ||||||
| in ``site-packages/django/bin`` within your Python installation. Consider | in ``site-packages/django/bin`` within your Python installation. Consider | ||||||
|   | |||||||
| @@ -613,6 +613,15 @@ Since :setting:`INSTALLED_APPS` now supports application configuration classes | |||||||
| in addition to application modules, you should review code that accesses this | in addition to application modules, you should review code that accesses this | ||||||
| setting directly and use the app registry (:attr:`django.apps.apps`) instead. | setting directly and use the app registry (:attr:`django.apps.apps`) instead. | ||||||
|  |  | ||||||
|  | If you're using Django in a plain Python script (not a management command) and | ||||||
|  | rely on the :envvar:`DJANGO_SETTINGS_MODULE` environment variable, you must | ||||||
|  | now explicitly initialize Django at the beginning of your script with:: | ||||||
|  |  | ||||||
|  |     >>> import django | ||||||
|  |     >>> django.setup() | ||||||
|  |  | ||||||
|  | Otherwise, you will most likely encounter a :exc:`~exceptions.RuntimeError`. | ||||||
|  |  | ||||||
| The "app registry" that manages the list of installed applications doesn't | The "app registry" that manages the list of installed applications doesn't | ||||||
| have the same features as the old "app cache". Even though the "app cache" was | have the same features as the old "app cache". Even though the "app cache" was | ||||||
| a private API, obsolete methods and arguments will be removed after a standard | a private API, obsolete methods and arguments will be removed after a standard | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import sys | |||||||
| import tempfile | import tempfile | ||||||
| import warnings | import warnings | ||||||
|  |  | ||||||
|  | import django | ||||||
| from django import contrib | from django import contrib | ||||||
| from django.utils._os import upath | from django.utils._os import upath | ||||||
| from django.utils import six | from django.utils import six | ||||||
| @@ -85,7 +86,6 @@ def get_installed(): | |||||||
|  |  | ||||||
|  |  | ||||||
| def setup(verbosity, test_labels): | def setup(verbosity, test_labels): | ||||||
|     import django |  | ||||||
|     from django.apps import apps, AppConfig |     from django.apps import apps, AppConfig | ||||||
|     from django.conf import settings |     from django.conf import settings | ||||||
|     from django.test import TransactionTestCase, TestCase |     from django.test import TransactionTestCase, TestCase | ||||||
| @@ -128,7 +128,7 @@ def setup(verbosity, test_labels): | |||||||
|     # Load all the ALWAYS_INSTALLED_APPS. |     # Load all the ALWAYS_INSTALLED_APPS. | ||||||
|     with warnings.catch_warnings(): |     with warnings.catch_warnings(): | ||||||
|         warnings.filterwarnings('ignore', 'django.contrib.comments is deprecated and will be removed before Django 1.8.', DeprecationWarning) |         warnings.filterwarnings('ignore', 'django.contrib.comments is deprecated and will be removed before Django 1.8.', DeprecationWarning) | ||||||
|         apps.populate_models() |         django.setup() | ||||||
|  |  | ||||||
|     # Load all the test model apps. |     # Load all the test model apps. | ||||||
|     test_modules = get_test_modules() |     test_modules = get_test_modules() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user