Commit 80d74097 authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

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.
parent 7ed20e01
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -6,3 +6,12 @@ def get_version(*args, **kwargs):
    # Only import if it's actually called.
    from django.utils.version import get_version
    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()
+2 −5
Original line number Diff line number Diff line
@@ -114,8 +114,6 @@ class AppConfig(object):
        Returns the model with the given case-insensitive model_name.

        Raises LookupError if no model exists with this name.

        This method assumes that apps.populate_models() has run.
        """
        if self.models is None:
            raise LookupError(
@@ -140,8 +138,6 @@ class AppConfig(object):

        Set the corresponding keyword argument to True to include such models.
        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():
            if model._deferred and not include_deferred:
@@ -156,7 +152,8 @@ class AppConfig(object):
        # Dictionary of models for this app, primarily maintained in the
        # 'all_models' attribute of the Apps this AppConfig is attached to.
        # 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

        if module_has_submodule(self.module, MODELS_MODULE_NAME):
+22 −22
Original line number Diff line number Diff line
@@ -3,7 +3,6 @@ import os
import sys
import warnings

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils import lru_cache
from django.utils.module_loading import import_lock
@@ -79,8 +78,6 @@ class Apps(object):
            # Application modules aren't expected to import anything, and
            # especially not other application modules, even indirectly.
            # Therefore we simply import them sequentially.
            if installed_apps is None:
                installed_apps = settings.INSTALLED_APPS
            for entry in installed_apps:
                if isinstance(entry, AppConfig):
                    app_config = entry
@@ -108,7 +105,9 @@ class Apps(object):
            if self._models_loaded:
                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
            # example to reference related objects. As a consequence:
@@ -144,6 +143,15 @@ class Apps(object):
                for app_config in self.get_app_configs():
                    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
    def ready(self):
        """
@@ -161,11 +169,7 @@ class Apps(object):
        If only_with_models_module in True (non-default), imports models and
        considers only applications containing a models module.
        """
        if only_with_models_module:
            self.populate_models()
        else:
            self.populate_apps()

        self.check_ready()
        for app_config in self.app_configs.values():
            if only_with_models_module and app_config.models_module is None:
                continue
@@ -180,11 +184,7 @@ class Apps(object):
        If only_with_models_module in True (non-default), imports models and
        considers only applications containing a models module.
        """
        if only_with_models_module:
            self.populate_models()
        else:
            self.populate_apps()

        self.check_ready()
        app_config = self.app_configs.get(app_label)
        if app_config is None:
            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.
        """
        self.populate_models()

        self.check_ready()
        if app_mod:
            warnings.warn(
                "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
        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())

    def register_model(self, app_label, model):
@@ -328,7 +327,8 @@ class Apps(object):
        imports safely (eg. that could lead to registering listeners twice),
        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.clear_cache()
        self._apps_loaded = False
@@ -340,7 +340,9 @@ class Apps(object):
        """
        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()

    def clear_cache(self):
@@ -429,9 +431,7 @@ class Apps(object):
        warnings.warn(
            "[a.path for a in get_app_configs()] supersedes get_app_paths().",
            PendingDeprecationWarning, stacklevel=2)

        self.populate_models()

        self.check_ready()
        app_paths = []
        for app in self.get_apps():
            app_paths.append(self._get_app_path(app))
+0 −1
Original line number Diff line number Diff line
@@ -160,7 +160,6 @@ class AdminSite(object):
        The default implementation checks that admin and contenttypes apps are
        installed, as well as the auth context processor.
        """
        apps.populate_apps()
        if not apps.has_app('django.contrib.admin'):
            raise ImproperlyConfigured("Put 'django.contrib.admin' in your "
                "INSTALLED_APPS setting in order to use the admin application.")
+0 −5
Original line number Diff line number Diff line
from django.apps import apps
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.db.models.fields import FieldDoesNotExist
@@ -15,10 +14,6 @@ __all__ = ['BaseValidator', 'InlineValidator']


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):
        for m in dir(self):
Loading