diff options
author | Arthur Koziel <arthur@arthurkoziel.com> | 2010-08-14 00:52:33 +0000 |
---|---|---|
committer | Arthur Koziel <arthur@arthurkoziel.com> | 2010-08-14 00:52:33 +0000 |
commit | 3da601b606fd2bc6da06e2749a30f1e4a8755cd3 (patch) | |
tree | db433a043534ef45465476887a6be5a66ef2e504 | |
parent | 81dc5079638a27853f5c92254133c59956e5f28f (diff) | |
download | django-3da601b606fd2bc6da06e2749a30f1e4a8755cd3.tar.gz |
[soc2010/app-loading] register models als unbound if the cache is not initialized
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/app-loading@13576 bcc190cf-cafb-0310-a4f2-bffc1f526a37
-rw-r--r-- | django/core/apps.py | 54 | ||||
-rw-r--r-- | tests/appcachetests/runtests.py | 106 | ||||
-rw-r--r-- | tests/appcachetests/same_label/model_app/models.py | 2 |
3 files changed, 101 insertions, 61 deletions
diff --git a/django/core/apps.py b/django/core/apps.py index b6435fd4e8..e59637c5dd 100644 --- a/django/core/apps.py +++ b/django/core/apps.py @@ -46,7 +46,7 @@ class AppCache(object): installed_apps = [], # Mapping of app_labels to a dictionary of model names to model code. - app_models = SortedDict(), + unbound_models = {}, # -- Everything below here is only used when populating the cache -- loaded = False, @@ -79,7 +79,19 @@ class AppCache(object): if not self.nesting_level: for app_name in self.postponed: self.load_app(app_name) + # since the cache is still unseeded at this point + # all models have been stored as unbound models + # we need to assign the models to the app instances + for app_name, models in self.unbound_models.iteritems(): + app_instance = self.find_app(app_name) + if not app_instance: + raise ImproperlyConfigured( + 'Could not find an app instance for "%s"' + % app_label) + for model in models.itervalues(): + app_instance.models.append(model) self.loaded = True + self.unbound_models = {} finally: self.write_lock.release() @@ -159,15 +171,6 @@ class AppCache(object): if app.name == name: return app - def create_app(self, name): - """create an app instance""" - name = name.split('.')[-1] - app = self.find_app(name) - if not app: - app = App(name) - self.app_instances.append(app) - return app - def app_cache_ready(self): """ Returns true if the model cache is fully populated. @@ -259,25 +262,34 @@ class AppCache(object): if seed_cache: self._populate() app = self.find_app(app_label) - if app: + if self.app_cache_ready() and not app: + return + if cache.app_cache_ready(): for model in app.models: if model_name.lower() == model._meta.object_name.lower(): return model + else: + return self.unbound_models.get(app_label, {}).get( + model_name.lower()) def register_models(self, app_label, *models): """ Register a set of models as belonging to an app. """ app_instance = self.find_app(app_label) - if not app_instance: - raise ImproperlyConfigured('Could not find App instance with label "%s". ' - 'Please check your INSTALLED_APPS setting' - % app_label) + if self.app_cache_ready() and not app_instance: + raise ImproperlyConfigured( + 'Could not find an app instance with the label "%s". ' + 'Please check your INSTALLED_APPS setting' % app_label) + for model in models: - # Store as 'name: model' pair in a dictionary - # in the models list of the App instance model_name = model._meta.object_name.lower() - model_dict = self.app_models.setdefault(app_label, SortedDict()) + if self.app_cache_ready(): + model_dict = dict([(model._meta.object_name.lower(), model) + for model in app_instance.models]) + else: + model_dict = self.unbound_models.setdefault(app_label, {}) + if model_name in model_dict: # The same model may be imported via different paths (e.g. # appname.models and project.appname.models). We use the source @@ -289,8 +301,10 @@ class AppCache(object): # comparing. if os.path.splitext(fname1)[0] == os.path.splitext(fname2)[0]: continue - model_dict[model_name] = model - app_instance.models.append(model) + if self.app_cache_ready(): + app_instance.models.append(model) + else: + model_dict[model_name] = model self._get_models_cache.clear() cache = AppCache() diff --git a/tests/appcachetests/runtests.py b/tests/appcachetests/runtests.py index 8b62e7e0dc..0a64d9f11a 100644 --- a/tests/appcachetests/runtests.py +++ b/tests/appcachetests/runtests.py @@ -32,15 +32,21 @@ class AppCacheTestCase(unittest.TestCase): # To detect which model modules have been imported, we go through # all loaded model classes and remove their respective module # from sys.modules - for app in cache.app_models.itervalues(): + for app in cache.unbound_models.itervalues(): for name in app.itervalues(): module = name.__module__ if module in sys.modules: del sys.modules[module] + for app in cache.app_instances: + for model in app.models: + module = model.__module__ + if module in sys.modules: + del sys.modules[module] + # we cannot copy() the whole cache.__dict__ in the setUp function # because thread.RLock is un(deep)copyable - cache.app_models = SortedDict() + cache.unbound_models = {} cache.app_instances = [] cache.installed_apps = [] @@ -178,34 +184,47 @@ class GetModelsTests(AppCacheTestCase): from django.contrib.flatpages.models import Site, FlatPage self.assertEqual(len(models), 3) self.assertEqual(models[0], Site) - self.assertEqual(models[1].__name__, 'FlatPage_sites') - self.assertEqual(models[2], FlatPage) + self.assertEqual(models[1], FlatPage) + self.assertEqual(models[2].__name__, 'FlatPage_sites') self.assertTrue(cache.app_cache_ready()) - def test_include_deferred(self): - """TODO!""" - class GetModelTests(AppCacheTestCase): """Tests for the get_model function""" - def test_get_model(self): - """Test that the correct model is returned""" - settings.INSTALLED_APPS = ('django.contrib.sites', - 'django.contrib.flatpages',) - rv = cache.get_model('flatpages', 'FlatPage') - from django.contrib.flatpages.models import FlatPage - self.assertEqual(rv, FlatPage) + def test_seeded(self): + """ + Test that the correct model is returned when the cache is seeded + """ + settings.INSTALLED_APPS = ('model_app',) + rv = cache.get_model('model_app', 'Person') + self.assertEqual(rv.__name__, 'Person') self.assertTrue(cache.app_cache_ready()) - def test_invalid(self): - """Test that None is returned if an app/model does not exist""" - self.assertEqual(cache.get_model('foo', 'bar'), None) + def test_seeded_invalid(self): + """ + Test that None is returned if a model was not registered + with the seeded cache + """ + rv = cache.get_model('model_app', 'Person') + self.assertEqual(rv, None) self.assertTrue(cache.app_cache_ready()) - def test_without_seeding(self): - """Test that None is returned if the cache is not seeded""" - settings.INSTALLED_APPS = ('django.contrib.flatpages',) - rv = cache.get_model('flatpages', 'FlatPage', seed_cache=False) + def test_unseeded(self): + """ + Test that the correct model is returned when the cache is + unseeded (but the model was registered using register_models) + """ + from model_app.models import Person + rv = cache.get_model('model_app', 'Person', seed_cache=False) + self.assertEqual(rv.__name__, 'Person') + self.assertFalse(cache.app_cache_ready()) + + def test_unseeded_invalid(self): + """ + Test that None is returned if a model was not registered + with the unseeded cache + """ + rv = cache.get_model('model_app', 'Person', seed_cache=False) self.assertEqual(rv, None) self.assertFalse(cache.app_cache_ready()) @@ -285,30 +304,37 @@ class LoadAppTests(AppCacheTestCase): class RegisterModelsTests(AppCacheTestCase): """Tests for the register_models function""" - def test_register_models(self): + def test_seeded_cache(self): """ - Test that register_models attaches the models to an existing - app instance + Test that the models are attached to the correct app instance + in a seeded cache """ - # We don't need to call the register_models method. Importing the - # models.py file will suffice. This is done in the load_app function - # The ModelBase will call the register_models method - cache.load_app('model_app') - app = cache.app_instances[0] - self.assertEqual(len(cache.app_instances), 1) - self.assertEqual(app.models[0].__name__, 'Person') + settings.INSTALLED_APPS = ('model_app',) + cache.get_app_errors() + self.assertTrue(cache.app_cache_ready()) + app_models = cache.app_instances[0].models + self.assertEqual(len(app_models), 1) + self.assertEqual(app_models[0].__name__, 'Person') - def test_app_not_installed(self): + def test_seeded_cache_invalid_app(self): """ - Test that an exception is raised if models are tried to be registered - to an app that isn't listed in INSTALLED_APPS. + Test that an exception is raised if the cache is seeded and models + are tried to be attached to an app instance that doesn't exist """ - try: - from model_app.models import Person - except ImproperlyConfigured: - pass - else: - self.fail('ImproperlyConfigured not raised') + settings.INSTALLED_APPS = ('model_app',) + cache.get_app_errors() + self.assertTrue(cache.app_cache_ready()) + from model_app.models import Person + self.assertRaises(ImproperlyConfigured, cache.register_models, + 'model_app_NONEXISTENT', *(Person,)) + + def test_unseeded_cache(self): + """ + Test that models can be registered with an unseeded cache + """ + from model_app.models import Person + self.assertFalse(cache.app_cache_ready()) + self.assertEquals(cache.unbound_models['model_app']['person'], Person) if __name__ == '__main__': unittest.main() diff --git a/tests/appcachetests/same_label/model_app/models.py b/tests/appcachetests/same_label/model_app/models.py index 94f391537f..ffb04e89c4 100644 --- a/tests/appcachetests/same_label/model_app/models.py +++ b/tests/appcachetests/same_label/model_app/models.py @@ -1,5 +1,5 @@ from django.db import models -class User(models.Model): +class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) |