Loading django/db/backends/sqlite3/schema.py +7 −8 Original line number Diff line number Diff line from django.db.backends.schema import BaseDatabaseSchemaEditor from django.db.models.loading import cache, default_cache, AppCache from django.db.models.fields.related import ManyToManyField from django.db.models.loading import BaseAppCache class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): Loading Loading @@ -38,20 +38,19 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): for field in delete_fields: del body[field.name] del mapping[field.column] # Work inside a new AppCache app_cache = BaseAppCache() # Construct a new model for the new state meta_contents = { 'app_label': model._meta.app_label, 'db_table': model._meta.db_table + "__new", 'unique_together': model._meta.unique_together if override_uniques is None else override_uniques, 'app_cache': app_cache, } meta = type("Meta", tuple(), meta_contents) body['Meta'] = meta body['__module__'] = model.__module__ self.app_cache = AppCache() cache.set_cache(self.app_cache) cache.copy_from(default_cache) temp_model = type(model._meta.object_name, model.__bases__, body) cache.set_cache(default_cache) # Create a new table with that format self.create_model(temp_model) # Copy data from the old table Loading django/db/models/base.py +8 −12 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ from django.db.models.query_utils import DeferredAttribute, deferred_class_facto from django.db.models.deletion import Collector from django.db.models.options import Options from django.db.models import signals from django.db.models.loading import register_models, get_model from django.utils.translation import ugettext_lazy as _ from django.utils.functional import curry from django.utils.encoding import force_str, force_text Loading Loading @@ -134,7 +133,7 @@ class ModelBase(type): new_class._base_manager = new_class._base_manager._copy_to_model(new_class) # Bail out early if we have already created this class. m = get_model(new_class._meta.app_label, name, m = new_class._meta.app_cache.get_model(new_class._meta.app_label, name, seed_cache=False, only_installed=False) if m is not None: return m Loading Loading @@ -242,16 +241,13 @@ class ModelBase(type): new_class._prepare() if new_class._meta.auto_register: register_models(new_class._meta.app_label, new_class) new_class._meta.app_cache.register_models(new_class._meta.app_label, new_class) # Because of the way imports happen (recursively), we may or may not be # the first time this model tries to register with the framework. There # should only be one class for each model, so we always return the # registered version. return get_model(new_class._meta.app_label, name, return new_class._meta.app_cache.get_model(new_class._meta.app_label, name, seed_cache=False, only_installed=False) else: return new_class def copy_managers(cls, base_managers): # This is in-place sorting of an Options attribute, but that's fine. Loading django/db/models/loading.py +79 −68 Original line number Diff line number Diff line Loading @@ -16,57 +16,52 @@ __all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models', 'load_app', 'app_cache_ready') class AppCache(object): def _initialize(): """ A cache that stores installed applications and their models. Used to provide reverse-relations and for app introspection (e.g. admin). Returns a dictionary to be used as the initial value of the [shared] state of the app cache. """ def __init__(self): return dict( # Keys of app_store are the model modules for each application. self.app_store = SortedDict() app_store = SortedDict(), # Mapping of installed app_labels to model modules for that app. self.app_labels = {} app_labels = {}, # Mapping of app_labels to a dictionary of model names to model code. # May contain apps that are not installed. self.app_models = SortedDict() app_models = SortedDict(), # Mapping of app_labels to errors raised when trying to import the app. self.app_errors = {} app_errors = {}, # -- Everything below here is only used when populating the cache -- self.loaded = False self.handled = {} self.postponed = [] self.nesting_level = 0 self._get_models_cache = {} loaded = False, handled = {}, postponed = [], nesting_level = 0, _get_models_cache = {}, ) class BaseAppCache(object): """ A cache that stores installed applications and their models. Used to provide reverse-relations and for app introspection (e.g. admin). This provides the base (non-Borg) AppCache class - the AppCache subclass adds borg-like behaviour for the few cases where it's needed, and adds the code that auto-loads from INSTALLED_APPS. """ def __init__(self): self.__dict__ = _initialize() def _populate(self): """ Fill in all the cache information. This method is threadsafe, in the sense that every caller will see the same state upon return, and if the cache is already initialised, it does no work. Stub method - this base class does no auto-loading. """ if self.loaded: return # Note that we want to use the import lock here - the app loading is # in many cases initiated implicitly by importing, and thus it is # possible to end up in deadlock when one thread initiates loading # without holding the importer lock and another thread then tries to # import something which also launches the app loading. For details of # this situation see #18251. imp.acquire_lock() try: if self.loaded: return for app_name in settings.INSTALLED_APPS: if app_name in self.handled: continue self.load_app(app_name, True) if not self.nesting_level: for app_name in self.postponed: self.load_app(app_name) self.loaded = True finally: imp.release_lock() def _label_for(self, app_mod): """ Loading Loading @@ -253,42 +248,58 @@ class AppCache(object): self.register_models(app_label, *models.values()) class AppCacheWrapper(object): """ As AppCache can be changed at runtime, this class wraps it so any imported references to 'cache' are changed along with it. class AppCache(BaseAppCache): """ A cache that stores installed applications and their models. Used to provide reverse-relations and for app introspection (e.g. admin). def __init__(self, cache): self._cache = cache Borg version of the BaseAppCache class. """ def set_cache(self, cache): self._cache = cache __shared_state = _initialize() def __getattr__(self, attr): if attr in ("_cache", "set_cache"): return self.__dict__[attr] return getattr(self._cache, attr) def __init__(self): self.__dict__ = self.__shared_state def __setattr__(self, attr, value): if attr in ("_cache", "set_cache"): self.__dict__[attr] = value def _populate(self): """ Fill in all the cache information. This method is threadsafe, in the sense that every caller will see the same state upon return, and if the cache is already initialised, it does no work. """ if self.loaded: return return setattr(self._cache, attr, value) # Note that we want to use the import lock here - the app loading is # in many cases initiated implicitly by importing, and thus it is # possible to end up in deadlock when one thread initiates loading # without holding the importer lock and another thread then tries to # import something which also launches the app loading. For details of # this situation see #18251. imp.acquire_lock() try: if self.loaded: return for app_name in settings.INSTALLED_APPS: if app_name in self.handled: continue self.load_app(app_name, True) if not self.nesting_level: for app_name in self.postponed: self.load_app(app_name) self.loaded = True finally: imp.release_lock() default_cache = AppCache() cache = AppCacheWrapper(default_cache) cache = AppCache() # These methods were always module level, so are kept that way for backwards # compatibility. These are wrapped with lambdas to stop the attribute # access resolving directly to a method on a single cache instance. get_apps = lambda *x, **y: cache.get_apps(*x, **y) get_app = lambda *x, **y: cache.get_app(*x, **y) get_app_errors = lambda *x, **y: cache.get_app_errors(*x, **y) get_models = lambda *x, **y: cache.get_models(*x, **y) get_model = lambda *x, **y: cache.get_model(*x, **y) register_models = lambda *x, **y: cache.register_models(*x, **y) load_app = lambda *x, **y: cache.load_app(*x, **y) app_cache_ready = lambda *x, **y: cache.app_cache_ready(*x, **y) # compatibility. get_apps = cache.get_apps get_app = cache.get_app get_app_errors = cache.get_app_errors get_models = cache.get_models get_model = cache.get_model register_models = cache.register_models load_app = cache.load_app app_cache_ready = cache.app_cache_ready django/db/models/options.py +4 −4 Original line number Diff line number Diff line Loading @@ -8,7 +8,7 @@ from django.conf import settings from django.db.models.fields.related import ManyToManyRel from django.db.models.fields import AutoField, FieldDoesNotExist from django.db.models.fields.proxy import OrderWrt from django.db.models.loading import get_models, app_cache_ready from django.db.models.loading import get_models, app_cache_ready, cache from django.utils import six from django.utils.functional import cached_property from django.utils.datastructures import SortedDict Loading @@ -21,7 +21,7 @@ get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]| DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering', 'unique_together', 'permissions', 'get_latest_by', 'order_with_respect_to', 'app_label', 'db_tablespace', 'abstract', 'managed', 'proxy', 'swappable', 'auto_created', 'index_together', 'auto_register') 'abstract', 'managed', 'proxy', 'swappable', 'auto_created', 'index_together', 'app_cache') @python_2_unicode_compatible Loading Loading @@ -70,8 +70,8 @@ class Options(object): # from *other* models. Needed for some admin checks. Internal use only. self.related_fkey_lookups = [] # If we should auto-register with the AppCache self.auto_register = True # A custom AppCache to use, if you're making a separate model set. self.app_cache = cache def contribute_to_class(self, cls, name): from django.db import connection Loading tests/schema/models.py +14 −11 Original line number Diff line number Diff line from django.db import models from django.db.models.loading import BaseAppCache # Because we want to test creation and deletion of these as separate things, # these models are all marked as unmanaged and only marked as managed while # a schema test is running. # these models are all inserted into a separate AppCache so the main test # runner doesn't syncdb them. new_app_cache = BaseAppCache() class Author(models.Model): Loading @@ -10,14 +13,14 @@ class Author(models.Model): height = models.PositiveIntegerField(null=True, blank=True) class Meta: auto_register = False app_cache = new_app_cache class AuthorWithM2M(models.Model): name = models.CharField(max_length=255) class Meta: auto_register = False app_cache = new_app_cache class Book(models.Model): Loading @@ -27,7 +30,7 @@ class Book(models.Model): # tags = models.ManyToManyField("Tag", related_name="books") class Meta: auto_register = False app_cache = new_app_cache class BookWithM2M(models.Model): Loading @@ -37,7 +40,7 @@ class BookWithM2M(models.Model): tags = models.ManyToManyField("Tag", related_name="books") class Meta: auto_register = False app_cache = new_app_cache class BookWithSlug(models.Model): Loading @@ -47,7 +50,7 @@ class BookWithSlug(models.Model): slug = models.CharField(max_length=20, unique=True) class Meta: auto_register = False app_cache = new_app_cache db_table = "schema_book" Loading @@ -56,7 +59,7 @@ class Tag(models.Model): slug = models.SlugField(unique=True) class Meta: auto_register = False app_cache = new_app_cache class TagUniqueRename(models.Model): Loading @@ -64,7 +67,7 @@ class TagUniqueRename(models.Model): slug2 = models.SlugField(unique=True) class Meta: auto_register = False app_cache = new_app_cache db_table = "schema_tag" Loading @@ -73,5 +76,5 @@ class UniqueTest(models.Model): slug = models.SlugField(unique=False) class Meta: auto_register = False app_cache = new_app_cache unique_together = ["year", "slug"] Loading
django/db/backends/sqlite3/schema.py +7 −8 Original line number Diff line number Diff line from django.db.backends.schema import BaseDatabaseSchemaEditor from django.db.models.loading import cache, default_cache, AppCache from django.db.models.fields.related import ManyToManyField from django.db.models.loading import BaseAppCache class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): Loading Loading @@ -38,20 +38,19 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): for field in delete_fields: del body[field.name] del mapping[field.column] # Work inside a new AppCache app_cache = BaseAppCache() # Construct a new model for the new state meta_contents = { 'app_label': model._meta.app_label, 'db_table': model._meta.db_table + "__new", 'unique_together': model._meta.unique_together if override_uniques is None else override_uniques, 'app_cache': app_cache, } meta = type("Meta", tuple(), meta_contents) body['Meta'] = meta body['__module__'] = model.__module__ self.app_cache = AppCache() cache.set_cache(self.app_cache) cache.copy_from(default_cache) temp_model = type(model._meta.object_name, model.__bases__, body) cache.set_cache(default_cache) # Create a new table with that format self.create_model(temp_model) # Copy data from the old table Loading
django/db/models/base.py +8 −12 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ from django.db.models.query_utils import DeferredAttribute, deferred_class_facto from django.db.models.deletion import Collector from django.db.models.options import Options from django.db.models import signals from django.db.models.loading import register_models, get_model from django.utils.translation import ugettext_lazy as _ from django.utils.functional import curry from django.utils.encoding import force_str, force_text Loading Loading @@ -134,7 +133,7 @@ class ModelBase(type): new_class._base_manager = new_class._base_manager._copy_to_model(new_class) # Bail out early if we have already created this class. m = get_model(new_class._meta.app_label, name, m = new_class._meta.app_cache.get_model(new_class._meta.app_label, name, seed_cache=False, only_installed=False) if m is not None: return m Loading Loading @@ -242,16 +241,13 @@ class ModelBase(type): new_class._prepare() if new_class._meta.auto_register: register_models(new_class._meta.app_label, new_class) new_class._meta.app_cache.register_models(new_class._meta.app_label, new_class) # Because of the way imports happen (recursively), we may or may not be # the first time this model tries to register with the framework. There # should only be one class for each model, so we always return the # registered version. return get_model(new_class._meta.app_label, name, return new_class._meta.app_cache.get_model(new_class._meta.app_label, name, seed_cache=False, only_installed=False) else: return new_class def copy_managers(cls, base_managers): # This is in-place sorting of an Options attribute, but that's fine. Loading
django/db/models/loading.py +79 −68 Original line number Diff line number Diff line Loading @@ -16,57 +16,52 @@ __all__ = ('get_apps', 'get_app', 'get_models', 'get_model', 'register_models', 'load_app', 'app_cache_ready') class AppCache(object): def _initialize(): """ A cache that stores installed applications and their models. Used to provide reverse-relations and for app introspection (e.g. admin). Returns a dictionary to be used as the initial value of the [shared] state of the app cache. """ def __init__(self): return dict( # Keys of app_store are the model modules for each application. self.app_store = SortedDict() app_store = SortedDict(), # Mapping of installed app_labels to model modules for that app. self.app_labels = {} app_labels = {}, # Mapping of app_labels to a dictionary of model names to model code. # May contain apps that are not installed. self.app_models = SortedDict() app_models = SortedDict(), # Mapping of app_labels to errors raised when trying to import the app. self.app_errors = {} app_errors = {}, # -- Everything below here is only used when populating the cache -- self.loaded = False self.handled = {} self.postponed = [] self.nesting_level = 0 self._get_models_cache = {} loaded = False, handled = {}, postponed = [], nesting_level = 0, _get_models_cache = {}, ) class BaseAppCache(object): """ A cache that stores installed applications and their models. Used to provide reverse-relations and for app introspection (e.g. admin). This provides the base (non-Borg) AppCache class - the AppCache subclass adds borg-like behaviour for the few cases where it's needed, and adds the code that auto-loads from INSTALLED_APPS. """ def __init__(self): self.__dict__ = _initialize() def _populate(self): """ Fill in all the cache information. This method is threadsafe, in the sense that every caller will see the same state upon return, and if the cache is already initialised, it does no work. Stub method - this base class does no auto-loading. """ if self.loaded: return # Note that we want to use the import lock here - the app loading is # in many cases initiated implicitly by importing, and thus it is # possible to end up in deadlock when one thread initiates loading # without holding the importer lock and another thread then tries to # import something which also launches the app loading. For details of # this situation see #18251. imp.acquire_lock() try: if self.loaded: return for app_name in settings.INSTALLED_APPS: if app_name in self.handled: continue self.load_app(app_name, True) if not self.nesting_level: for app_name in self.postponed: self.load_app(app_name) self.loaded = True finally: imp.release_lock() def _label_for(self, app_mod): """ Loading Loading @@ -253,42 +248,58 @@ class AppCache(object): self.register_models(app_label, *models.values()) class AppCacheWrapper(object): """ As AppCache can be changed at runtime, this class wraps it so any imported references to 'cache' are changed along with it. class AppCache(BaseAppCache): """ A cache that stores installed applications and their models. Used to provide reverse-relations and for app introspection (e.g. admin). def __init__(self, cache): self._cache = cache Borg version of the BaseAppCache class. """ def set_cache(self, cache): self._cache = cache __shared_state = _initialize() def __getattr__(self, attr): if attr in ("_cache", "set_cache"): return self.__dict__[attr] return getattr(self._cache, attr) def __init__(self): self.__dict__ = self.__shared_state def __setattr__(self, attr, value): if attr in ("_cache", "set_cache"): self.__dict__[attr] = value def _populate(self): """ Fill in all the cache information. This method is threadsafe, in the sense that every caller will see the same state upon return, and if the cache is already initialised, it does no work. """ if self.loaded: return return setattr(self._cache, attr, value) # Note that we want to use the import lock here - the app loading is # in many cases initiated implicitly by importing, and thus it is # possible to end up in deadlock when one thread initiates loading # without holding the importer lock and another thread then tries to # import something which also launches the app loading. For details of # this situation see #18251. imp.acquire_lock() try: if self.loaded: return for app_name in settings.INSTALLED_APPS: if app_name in self.handled: continue self.load_app(app_name, True) if not self.nesting_level: for app_name in self.postponed: self.load_app(app_name) self.loaded = True finally: imp.release_lock() default_cache = AppCache() cache = AppCacheWrapper(default_cache) cache = AppCache() # These methods were always module level, so are kept that way for backwards # compatibility. These are wrapped with lambdas to stop the attribute # access resolving directly to a method on a single cache instance. get_apps = lambda *x, **y: cache.get_apps(*x, **y) get_app = lambda *x, **y: cache.get_app(*x, **y) get_app_errors = lambda *x, **y: cache.get_app_errors(*x, **y) get_models = lambda *x, **y: cache.get_models(*x, **y) get_model = lambda *x, **y: cache.get_model(*x, **y) register_models = lambda *x, **y: cache.register_models(*x, **y) load_app = lambda *x, **y: cache.load_app(*x, **y) app_cache_ready = lambda *x, **y: cache.app_cache_ready(*x, **y) # compatibility. get_apps = cache.get_apps get_app = cache.get_app get_app_errors = cache.get_app_errors get_models = cache.get_models get_model = cache.get_model register_models = cache.register_models load_app = cache.load_app app_cache_ready = cache.app_cache_ready
django/db/models/options.py +4 −4 Original line number Diff line number Diff line Loading @@ -8,7 +8,7 @@ from django.conf import settings from django.db.models.fields.related import ManyToManyRel from django.db.models.fields import AutoField, FieldDoesNotExist from django.db.models.fields.proxy import OrderWrt from django.db.models.loading import get_models, app_cache_ready from django.db.models.loading import get_models, app_cache_ready, cache from django.utils import six from django.utils.functional import cached_property from django.utils.datastructures import SortedDict Loading @@ -21,7 +21,7 @@ get_verbose_name = lambda class_name: re.sub('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]| DEFAULT_NAMES = ('verbose_name', 'verbose_name_plural', 'db_table', 'ordering', 'unique_together', 'permissions', 'get_latest_by', 'order_with_respect_to', 'app_label', 'db_tablespace', 'abstract', 'managed', 'proxy', 'swappable', 'auto_created', 'index_together', 'auto_register') 'abstract', 'managed', 'proxy', 'swappable', 'auto_created', 'index_together', 'app_cache') @python_2_unicode_compatible Loading Loading @@ -70,8 +70,8 @@ class Options(object): # from *other* models. Needed for some admin checks. Internal use only. self.related_fkey_lookups = [] # If we should auto-register with the AppCache self.auto_register = True # A custom AppCache to use, if you're making a separate model set. self.app_cache = cache def contribute_to_class(self, cls, name): from django.db import connection Loading
tests/schema/models.py +14 −11 Original line number Diff line number Diff line from django.db import models from django.db.models.loading import BaseAppCache # Because we want to test creation and deletion of these as separate things, # these models are all marked as unmanaged and only marked as managed while # a schema test is running. # these models are all inserted into a separate AppCache so the main test # runner doesn't syncdb them. new_app_cache = BaseAppCache() class Author(models.Model): Loading @@ -10,14 +13,14 @@ class Author(models.Model): height = models.PositiveIntegerField(null=True, blank=True) class Meta: auto_register = False app_cache = new_app_cache class AuthorWithM2M(models.Model): name = models.CharField(max_length=255) class Meta: auto_register = False app_cache = new_app_cache class Book(models.Model): Loading @@ -27,7 +30,7 @@ class Book(models.Model): # tags = models.ManyToManyField("Tag", related_name="books") class Meta: auto_register = False app_cache = new_app_cache class BookWithM2M(models.Model): Loading @@ -37,7 +40,7 @@ class BookWithM2M(models.Model): tags = models.ManyToManyField("Tag", related_name="books") class Meta: auto_register = False app_cache = new_app_cache class BookWithSlug(models.Model): Loading @@ -47,7 +50,7 @@ class BookWithSlug(models.Model): slug = models.CharField(max_length=20, unique=True) class Meta: auto_register = False app_cache = new_app_cache db_table = "schema_book" Loading @@ -56,7 +59,7 @@ class Tag(models.Model): slug = models.SlugField(unique=True) class Meta: auto_register = False app_cache = new_app_cache class TagUniqueRename(models.Model): Loading @@ -64,7 +67,7 @@ class TagUniqueRename(models.Model): slug2 = models.SlugField(unique=True) class Meta: auto_register = False app_cache = new_app_cache db_table = "schema_tag" Loading @@ -73,5 +76,5 @@ class UniqueTest(models.Model): slug = models.SlugField(unique=False) class Meta: auto_register = False app_cache = new_app_cache unique_together = ["year", "slug"]