Skip to content

Commit

Permalink
[soc2010/app-loading] implement APP_CLASSES setting
Browse files Browse the repository at this point in the history
  • Loading branch information
arthurk committed Sep 12, 2010
1 parent 574424f commit 11a32d6
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 30 deletions.
3 changes: 3 additions & 0 deletions django/conf/global_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@
EMAIL_HOST_PASSWORD = ''
EMAIL_USE_TLS = False

# List of strings representing installed app classes.
APP_CLASSES = ()

# List of strings representing installed apps.
INSTALLED_APPS = ()

Expand Down
39 changes: 18 additions & 21 deletions django/core/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def __str__(self):
return self.name

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

class AppCache(object):
"""
Expand Down Expand Up @@ -78,10 +78,19 @@ def _populate(self):
try:
if self.loaded:
return
for app_name in settings.APP_CLASSES:
if app_name in self.handled:
continue
app_module, app_classname = app_name.rsplit('.', 1)
app_module = import_module(app_module)
app_class = getattr(app_module, app_classname)
app_name = app_name.rsplit('.', 2)[0]
self.load_app(app_name, True, app_class)
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)
Expand Down Expand Up @@ -111,28 +120,15 @@ def _populate(self):
finally:
self.write_lock.release()

def load_app(self, app_name, can_postpone=False):
def load_app(self, app_name, can_postpone=False, app_class=App):
"""
Loads the app with the provided fully qualified name, and returns the
model module.
"""
self.handled[app_name] = None
self.nesting_level += 1

try:
app_module = import_module(app_name)
except ImportError:
# If the import fails, we assume it was because an path to a
# class was passed (e.g. "foo.bar.MyApp")
# We split the app_name by the rightmost dot to get the path
# and classname, and then try importing it again
if not '.' in app_name:
raise
app_name, app_classname = app_name.rsplit('.', 1)
app_module = import_module(app_name)
app_class = getattr(app_module, app_classname)
else:
app_class = App
app_module = import_module(app_name)

# check if an app instance with that name already exists
app_instance = self.find_app(app_name)
Expand All @@ -144,10 +140,11 @@ def load_app(self, app_name, can_postpone=False):
app_instance_name = app_name
app_instance = app_class(app_instance_name)
app_instance.module = app_module
app_instance.path = app_name
self.app_instances.append(app_instance)
self.installed_apps.append(app_name)

# check if the app instance specifies a path to models
# Check if the app instance specifies a path to models
# if not, we use the models.py file from the package dir
try:
models_path = app_instance.models_path
Expand Down Expand Up @@ -179,7 +176,7 @@ def load_app(self, app_name, can_postpone=False):
self.nesting_level -= 1
app_instance.models_module = models
return models

def find_app(self, name):
"Returns the App instance that matches name"
if '.' in name:
Expand Down Expand Up @@ -211,9 +208,9 @@ def get_app(self, app_label, emptyOK=False):
self._populate()
self.write_lock.acquire()
try:
for app_name in self.installed_apps:
if app_label == app_name.split('.')[-1]:
mod = self.load_app(app_name, False)
for app in self.app_instances:
if app_label == app.name:
mod = self.load_app(app.path, False)
if mod is None:
if emptyOK:
return None
Expand Down
8 changes: 7 additions & 1 deletion django/db/models/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,13 @@ def contribute_to_class(self, cls, name):
# If the db_table wasn't provided, use the db_prefix + module_name.
if not self.db_table:
app_instance = cache.find_app(self.app_label)
self.db_table = "%s_%s" % (app_instance.db_prefix, self.module_name)
if not app_instance:
# use the app label when no app instance was found, this
# can happen when the app cache is not initialized but the
# model is imported
self.db_table = "%s_%s" % (self.app_label, self.module_name)
else:
self.db_table = "%s_%s" % (app_instance.db_prefix, self.module_name)
self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())

def _prepare(self, model):
Expand Down
21 changes: 13 additions & 8 deletions tests/appcachetests/runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ class AppCacheTestCase(unittest.TestCase):

def setUp(self):
self.old_installed_apps = settings.INSTALLED_APPS
self.old_app_classes = settings.APP_CLASSES
settings.APP_CLASSES = ()
settings.INSTALLED_APPS = ()

def tearDown(self):
settings.INSTALLED_APPS = self.old_installed_apps
settings.APP_CLASSES = self.old_app_classes

# The appcache imports models modules. We need to delete the
# imported module from sys.modules after the test has run.
Expand Down Expand Up @@ -102,7 +105,7 @@ def test_db_prefix_exception(self):
Test that an exception is raised if two app instances
have the same db_prefix attribute
"""
settings.INSTALLED_APPS = ('nomodel_app.MyApp', 'model_app.MyOtherApp',)
settings.APP_CLASSES = ('nomodel_app.MyApp', 'model_app.MyOtherApp',)
self.assertRaises(ImproperlyConfigured, cache.get_apps)

class GetAppTests(AppCacheTestCase):
Expand Down Expand Up @@ -264,10 +267,10 @@ def test_without_models(self):
def test_custom_app(self):
"""
Test that a custom app instance is created if the function
gets passed a classname
gets passed a custom app class
"""
from nomodel_app import MyApp
rv = cache.load_app('nomodel_app.MyApp')
rv = cache.load_app('nomodel_app', False, MyApp)
app = cache.app_instances[0]
self.assertEqual(len(cache.app_instances), 1)
self.assertEqual(app.name, 'nomodel_app')
Expand All @@ -278,10 +281,11 @@ def test_custom_models_path(self):
"""
Test that custom models are imported correctly
"""
rv = cache.load_app('model_app.MyApp')
from nomodel_app import MyApp
rv = cache.load_app('model_app', False, MyApp)
app = cache.app_instances[0]
self.assertEqual(app.models_module.__name__, 'model_app.othermodels')
self.assertEqual(rv.__name__, 'model_app.othermodels')
self.assertEqual(app.models_module.__name__, 'model_app.models')
self.assertEqual(rv.__name__, 'model_app.models')

def test_twice(self):
"""
Expand All @@ -304,10 +308,11 @@ def test_installed_apps(self):
"""
Test that the installed_apps attribute is populated correctly
"""
settings.INSTALLED_APPS = ('model_app', 'nomodel_app.MyApp',)
settings.APP_CLASSES = ('nomodel_app.MyApp',)
settings.INSTALLED_APPS = ('model_app',)
# populate cache
cache.get_app_errors()
self.assertEqual(cache.installed_apps, ['model_app', 'nomodel_app',])
self.assertEqual(cache.installed_apps, ['nomodel_app', 'model_app',])

class RegisterModelsTests(AppCacheTestCase):
"""Tests for the register_models function"""
Expand Down

0 comments on commit 11a32d6

Please sign in to comment.