Commit bbdf01e0 authored by Aymeric Augustin's avatar Aymeric Augustin
Browse files

Populated non-master app registries.

This removes the gap between the master app registry and ad-hoc app
registries created by the migration framework, specifically in terms
of behavior of the get_model[s] methods.

This commit contains a stealth feature that I'd rather not describe.
parent 81a5e35c
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ class AppConfig(object):
        self.models = None

    def __repr__(self):
        return '<AppConfig: %s>' % self.label
        return '<%s: %s>' % (self.__class__.__name__, self.label)

    @classmethod
    def create(cls, entry):
+10 −8
Original line number Diff line number Diff line
@@ -27,9 +27,8 @@ class Apps(object):
        if master and hasattr(sys.modules[__name__], 'apps'):
            raise RuntimeError("You may create only one master registry.")

        # When master is set to False, the registry isn't populated from
        # INSTALLED_APPS and ignores the only_installed arguments to
        # get_model[s].
        # When master is set to False, the registry ignores the only_installed
        # arguments to get_model[s].
        self.master = master

        # Mapping of app labels => model names => model classes. Every time a
@@ -48,9 +47,9 @@ class Apps(object):
        # set_available_apps and set_installed_apps.
        self.stored_app_configs = []

        # Internal flags used when populating the master registry.
        self._apps_loaded = not self.master
        self._models_loaded = not self.master
        # Internal flags used when populating the registry.
        self._apps_loaded = False
        self._models_loaded = False

        # Pending lookups for lazy relations.
        self._pending_lookups = {}
@@ -82,8 +81,11 @@ class Apps(object):
            # Therefore we simply import them sequentially.
            if installed_apps is None:
                installed_apps = settings.INSTALLED_APPS
            for app_name in installed_apps:
                app_config = AppConfig.create(app_name)
            for entry in installed_apps:
                if isinstance(entry, AppConfig):
                    app_config = entry
                else:
                    app_config = AppConfig.create(entry)
                self.app_configs[app_config.label] = app_config

            self.get_models.cache_clear()
+19 −0
Original line number Diff line number Diff line
from django.apps import AppConfig
from django.apps.registry import Apps
from django.db import models
from django.db.models.options import DEFAULT_NAMES, normalize_unique_together
@@ -33,6 +34,11 @@ class ProjectState(object):
        "Turns the project state into actual models in a new Apps"
        if self.apps is None:
            self.apps = Apps()
            # Populate the app registry with a stub for each application.
            app_labels = set(model_state.app_label for model_state in self.models.values())
            app_configs = [AppConfigStub(label) for label in sorted(app_labels)]
            self.apps.populate_apps(app_configs)
            self.apps.populate_models()
            # We keep trying to render the models in a loop, ignoring invalid
            # base errors, until the size of the unrendered models doesn't
            # decrease by at least one, meaning there's a base dependency loop/
@@ -68,6 +74,19 @@ class ProjectState(object):
        return not (self == other)


class AppConfigStub(AppConfig):
    """
    Stubs a Django AppConfig. Only provides a label and a dict of models.
    """
    def __init__(self, label):
        self.label = label
        self.path = None
        super(AppConfigStub, self).__init__(None, None)

    def import_models(self, all_models):
        self.models = all_models


class ModelState(object):
    """
    Represents a Django Model. We don't use the actual Model class
+4 −2
Original line number Diff line number Diff line
@@ -50,8 +50,10 @@ class AppsTests(TestCase):
        Tests the ready property of a registry other than the master.
        """
        apps = Apps()
        # Currently, non-master app registries are artificially considered
        # ready regardless of whether populate_models() has run.
        self.assertFalse(apps.ready)
        apps.populate_apps([])
        self.assertFalse(apps.ready)
        apps.populate_models()
        self.assertTrue(apps.ready)

    def test_bad_app_config(self):