Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Rest of the _meta.app_cache stuff. Schema tests work now.

  • Loading branch information...
commit 75bf394d86b802de670502400e9ab7eca2808935 1 parent 104ad05
Andrew Godwin andrewgodwin authored
11 django/db/models/fields/related.py
View
@@ -2,7 +2,7 @@
from django.db import connection, connections, router
from django.db.backends import util
-from django.db.models import signals, get_model
+from django.db.models import signals
from django.db.models.fields import (AutoField, Field, IntegerField,
PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist)
from django.db.models.related import RelatedObject, PathInfo
@@ -18,8 +18,6 @@
RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
-pending_lookups = {}
-
def add_lazy_relation(cls, field, relation, operation):
"""
@@ -70,14 +68,14 @@ class MyModel(Model):
# string right away. If get_model returns None, it means that the related
# model isn't loaded yet, so we need to pend the relation until the class
# is prepared.
- model = get_model(app_label, model_name,
+ model = cls._meta.app_cache.get_model(app_label, model_name,
seed_cache=False, only_installed=False)
if model:
operation(field, model, cls)
else:
key = (app_label, model_name)
value = (cls, field, operation)
- pending_lookups.setdefault(key, []).append(value)
+ cls._meta.app_cache.pending_lookups.setdefault(key, []).append(value)
def do_pending_lookups(sender, **kwargs):
@@ -85,7 +83,7 @@ def do_pending_lookups(sender, **kwargs):
Handle any pending relations to the sending model. Sent from class_prepared.
"""
key = (sender._meta.app_label, sender.__name__)
- for cls, field, operation in pending_lookups.pop(key, []):
+ for cls, field, operation in sender._meta.app_cache.pending_lookups.pop(key, []):
operation(field, sender, cls)
signals.class_prepared.connect(do_pending_lookups)
@@ -1330,6 +1328,7 @@ def set_managed(field, model, cls):
'unique_together': (from_, to),
'verbose_name': '%(from)s-%(to)s relationship' % {'from': from_, 'to': to},
'verbose_name_plural': '%(from)s-%(to)s relationships' % {'from': from_, 'to': to},
+ 'app_cache': field.model._meta.app_cache,
})
# Construct and return the new class.
return type(str(name), (models.Model,), {
80 django/db/models/loading.py
View
@@ -35,7 +35,11 @@ def _initialize():
# Mapping of app_labels to errors raised when trying to import the app.
app_errors = {},
+ # Pending lookups for lazy relations
+ pending_lookups = {},
+
# -- Everything below here is only used when populating the cache --
+ loads_installed = True,
loaded = False,
handled = {},
postponed = [],
@@ -56,12 +60,44 @@ class BaseAppCache(object):
def __init__(self):
self.__dict__ = _initialize()
+ # This stops _populate loading from INSTALLED_APPS and ignores the
+ # only_installed arguments to get_model[s]
+ self.loads_installed = False
def _populate(self):
"""
Stub method - this base class does no auto-loading.
"""
- self.loaded = True
+ """
+ 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
+ if not self.loads_installed:
+ self.loaded = True
+ 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):
"""
@@ -169,12 +205,15 @@ def get_models(self, app_mod=None,
By default, models that aren't part of installed apps will *not*
be included in the list of models. However, if you specify
- only_installed=False, they will be.
+ only_installed=False, they will be. If you're using a non-default
+ AppCache, this argument does nothing - all models will be included.
By default, models that have been swapped out will *not* be
included in the list of models. However, if you specify
include_swapped, they will be.
"""
+ if not self.loads_installed:
+ only_installed = False
cache_key = (app_mod, include_auto_created, include_deferred, only_installed, include_swapped)
try:
return self._get_models_cache[cache_key]
@@ -212,6 +251,8 @@ def get_model(self, app_label, model_name,
Returns None if no model is found.
"""
+ if not self.loads_installed:
+ only_installed = False
if seed_cache:
self._populate()
if only_installed and app_label not in self.app_labels:
@@ -241,12 +282,6 @@ def register_models(self, app_label, *models):
model_dict[model_name] = model
self._get_models_cache.clear()
- def copy_from(self, other):
- "Registers all models from the other cache into this one"
- cache._populate()
- for app_label, models in other.app_models.items():
- self.register_models(app_label, *models.values())
-
class AppCache(BaseAppCache):
"""
@@ -261,35 +296,6 @@ class AppCache(BaseAppCache):
def __init__(self):
self.__dict__ = self.__shared_state
- 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
- # 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()
-
cache = AppCache()
0  tests/app_cache/__init__.py
View
No changes.
17 tests/app_cache/models.py
View
@@ -0,0 +1,17 @@
+from django.db import models
+from django.db.models.loading import BaseAppCache
+
+# We're testing app cache presence on load, so this is handy.
+
+new_app_cache = BaseAppCache()
+
+
+class TotallyNormal(models.Model):
+ name = models.CharField(max_length=255)
+
+
+class SoAlternative(models.Model):
+ name = models.CharField(max_length=255)
+
+ class Meta:
+ app_cache = new_app_cache
50 tests/app_cache/tests.py
View
@@ -0,0 +1,50 @@
+from __future__ import absolute_import
+import datetime
+from django.test import TransactionTestCase
+from django.utils.unittest import skipUnless
+from django.db import connection, DatabaseError, IntegrityError
+from django.db.models.fields import IntegerField, TextField, CharField, SlugField
+from django.db.models.fields.related import ManyToManyField, ForeignKey
+from django.db.models.loading import cache, BaseAppCache
+from django.db import models
+from .models import TotallyNormal, SoAlternative, new_app_cache
+
+
+class AppCacheTests(TransactionTestCase):
+ """
+ Tests the AppCache borg and non-borg versions
+ """
+
+ def test_models_py(self):
+ """
+ Tests that the models in the models.py file were loaded correctly.
+ """
+
+ self.assertEqual(cache.get_model("app_cache", "TotallyNormal"), TotallyNormal)
+ self.assertEqual(cache.get_model("app_cache", "SoAlternative"), None)
+
+ self.assertEqual(new_app_cache.get_model("app_cache", "TotallyNormal"), None)
+ self.assertEqual(new_app_cache.get_model("app_cache", "SoAlternative"), SoAlternative)
+
+ def test_dynamic_load(self):
+ """
+ Makes a new model at runtime and ensures it goes into the right place.
+ """
+ old_models = cache.get_models(cache.get_app("app_cache"))
+ # Construct a new model in a new app cache
+ body = {}
+ new_app_cache = BaseAppCache()
+ meta_contents = {
+ 'app_label': "app_cache",
+ 'app_cache': new_app_cache,
+ }
+ meta = type("Meta", tuple(), meta_contents)
+ body['Meta'] = meta
+ body['__module__'] = TotallyNormal.__module__
+ temp_model = type("SouthPonies", (models.Model,), body)
+ # Make sure it appeared in the right place!
+ self.assertEqual(
+ old_models,
+ cache.get_models(cache.get_app("app_cache")),
+ )
+ self.assertEqual(new_app_cache.get_model("app_cache", "SouthPonies"), temp_model)
Please sign in to comment.
Something went wrong with that request. Please try again.