Skip to content

Commit 11a32d6

Browse files
committed
[soc2010/app-loading] implement APP_CLASSES setting
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/app-loading@13759 bcc190cf-cafb-0310-a4f2-bffc1f526a37
1 parent 574424f commit 11a32d6

File tree

4 files changed

+41
-30
lines changed

4 files changed

+41
-30
lines changed

django/conf/global_settings.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@
170170
EMAIL_HOST_PASSWORD = ''
171171
EMAIL_USE_TLS = False
172172

173+
# List of strings representing installed app classes.
174+
APP_CLASSES = ()
175+
173176
# List of strings representing installed apps.
174177
INSTALLED_APPS = ()
175178

django/core/apps.py

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def __str__(self):
3333
return self.name
3434

3535
def __repr__(self):
36-
return '<App: %s>' % self.name
36+
return '<%s: %s>' % (self.__class__.__name__, self.name)
3737

3838
class AppCache(object):
3939
"""
@@ -78,10 +78,19 @@ def _populate(self):
7878
try:
7979
if self.loaded:
8080
return
81+
for app_name in settings.APP_CLASSES:
82+
if app_name in self.handled:
83+
continue
84+
app_module, app_classname = app_name.rsplit('.', 1)
85+
app_module = import_module(app_module)
86+
app_class = getattr(app_module, app_classname)
87+
app_name = app_name.rsplit('.', 2)[0]
88+
self.load_app(app_name, True, app_class)
8189
for app_name in settings.INSTALLED_APPS:
8290
if app_name in self.handled:
8391
continue
8492
self.load_app(app_name, True)
93+
8594
if not self.nesting_level:
8695
for app_name in self.postponed:
8796
self.load_app(app_name)
@@ -111,28 +120,15 @@ def _populate(self):
111120
finally:
112121
self.write_lock.release()
113122

114-
def load_app(self, app_name, can_postpone=False):
123+
def load_app(self, app_name, can_postpone=False, app_class=App):
115124
"""
116125
Loads the app with the provided fully qualified name, and returns the
117126
model module.
118127
"""
119128
self.handled[app_name] = None
120129
self.nesting_level += 1
121130

122-
try:
123-
app_module = import_module(app_name)
124-
except ImportError:
125-
# If the import fails, we assume it was because an path to a
126-
# class was passed (e.g. "foo.bar.MyApp")
127-
# We split the app_name by the rightmost dot to get the path
128-
# and classname, and then try importing it again
129-
if not '.' in app_name:
130-
raise
131-
app_name, app_classname = app_name.rsplit('.', 1)
132-
app_module = import_module(app_name)
133-
app_class = getattr(app_module, app_classname)
134-
else:
135-
app_class = App
131+
app_module = import_module(app_name)
136132

137133
# check if an app instance with that name already exists
138134
app_instance = self.find_app(app_name)
@@ -144,10 +140,11 @@ def load_app(self, app_name, can_postpone=False):
144140
app_instance_name = app_name
145141
app_instance = app_class(app_instance_name)
146142
app_instance.module = app_module
143+
app_instance.path = app_name
147144
self.app_instances.append(app_instance)
148145
self.installed_apps.append(app_name)
149146

150-
# check if the app instance specifies a path to models
147+
# Check if the app instance specifies a path to models
151148
# if not, we use the models.py file from the package dir
152149
try:
153150
models_path = app_instance.models_path
@@ -179,7 +176,7 @@ def load_app(self, app_name, can_postpone=False):
179176
self.nesting_level -= 1
180177
app_instance.models_module = models
181178
return models
182-
179+
183180
def find_app(self, name):
184181
"Returns the App instance that matches name"
185182
if '.' in name:
@@ -211,9 +208,9 @@ def get_app(self, app_label, emptyOK=False):
211208
self._populate()
212209
self.write_lock.acquire()
213210
try:
214-
for app_name in self.installed_apps:
215-
if app_label == app_name.split('.')[-1]:
216-
mod = self.load_app(app_name, False)
211+
for app in self.app_instances:
212+
if app_label == app.name:
213+
mod = self.load_app(app.path, False)
217214
if mod is None:
218215
if emptyOK:
219216
return None

django/db/models/options.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,13 @@ def contribute_to_class(self, cls, name):
9999
# If the db_table wasn't provided, use the db_prefix + module_name.
100100
if not self.db_table:
101101
app_instance = cache.find_app(self.app_label)
102-
self.db_table = "%s_%s" % (app_instance.db_prefix, self.module_name)
102+
if not app_instance:
103+
# use the app label when no app instance was found, this
104+
# can happen when the app cache is not initialized but the
105+
# model is imported
106+
self.db_table = "%s_%s" % (self.app_label, self.module_name)
107+
else:
108+
self.db_table = "%s_%s" % (app_instance.db_prefix, self.module_name)
103109
self.db_table = truncate_name(self.db_table, connection.ops.max_name_length())
104110

105111
def _prepare(self, model):

tests/appcachetests/runtests.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@ class AppCacheTestCase(unittest.TestCase):
1818

1919
def setUp(self):
2020
self.old_installed_apps = settings.INSTALLED_APPS
21+
self.old_app_classes = settings.APP_CLASSES
22+
settings.APP_CLASSES = ()
2123
settings.INSTALLED_APPS = ()
2224

2325
def tearDown(self):
2426
settings.INSTALLED_APPS = self.old_installed_apps
27+
settings.APP_CLASSES = self.old_app_classes
2528

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

108111
class GetAppTests(AppCacheTestCase):
@@ -264,10 +267,10 @@ def test_without_models(self):
264267
def test_custom_app(self):
265268
"""
266269
Test that a custom app instance is created if the function
267-
gets passed a classname
270+
gets passed a custom app class
268271
"""
269272
from nomodel_app import MyApp
270-
rv = cache.load_app('nomodel_app.MyApp')
273+
rv = cache.load_app('nomodel_app', False, MyApp)
271274
app = cache.app_instances[0]
272275
self.assertEqual(len(cache.app_instances), 1)
273276
self.assertEqual(app.name, 'nomodel_app')
@@ -278,10 +281,11 @@ def test_custom_models_path(self):
278281
"""
279282
Test that custom models are imported correctly
280283
"""
281-
rv = cache.load_app('model_app.MyApp')
284+
from nomodel_app import MyApp
285+
rv = cache.load_app('model_app', False, MyApp)
282286
app = cache.app_instances[0]
283-
self.assertEqual(app.models_module.__name__, 'model_app.othermodels')
284-
self.assertEqual(rv.__name__, 'model_app.othermodels')
287+
self.assertEqual(app.models_module.__name__, 'model_app.models')
288+
self.assertEqual(rv.__name__, 'model_app.models')
285289

286290
def test_twice(self):
287291
"""
@@ -304,10 +308,11 @@ def test_installed_apps(self):
304308
"""
305309
Test that the installed_apps attribute is populated correctly
306310
"""
307-
settings.INSTALLED_APPS = ('model_app', 'nomodel_app.MyApp',)
311+
settings.APP_CLASSES = ('nomodel_app.MyApp',)
312+
settings.INSTALLED_APPS = ('model_app',)
308313
# populate cache
309314
cache.get_app_errors()
310-
self.assertEqual(cache.installed_apps, ['model_app', 'nomodel_app',])
315+
self.assertEqual(cache.installed_apps, ['nomodel_app', 'model_app',])
311316

312317
class RegisterModelsTests(AppCacheTestCase):
313318
"""Tests for the register_models function"""

0 commit comments

Comments
 (0)