Added the ability to supply custom app configs.
This commit is contained in:
parent
517c24bcfa
commit
99bd39ef6e
|
@ -1,6 +1,7 @@
|
|||
from importlib import import_module
|
||||
|
||||
from django.utils.module_loading import module_has_submodule
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.module_loading import import_by_path, module_has_submodule
|
||||
from django.utils._os import upath
|
||||
|
||||
|
||||
|
@ -14,16 +15,33 @@ class AppConfig(object):
|
|||
|
||||
def __init__(self, app_name):
|
||||
# Full Python path to the application eg. 'django.contrib.admin'.
|
||||
# This is the value that appears in INSTALLED_APPS.
|
||||
self.name = app_name
|
||||
|
||||
# Root module for the application eg. <module 'django.contrib.admin'
|
||||
# from 'django/contrib/admin/__init__.pyc'>.
|
||||
self.app_module = import_module(app_name)
|
||||
|
||||
# The following attributes could be defined at the class level in a
|
||||
# subclass, hence the test-and-set pattern.
|
||||
|
||||
# Last component of the Python path to the application eg. 'admin'.
|
||||
# This value must be unique across a Django project.
|
||||
self.label = app_name.rpartition(".")[2]
|
||||
if not hasattr(self, 'label'):
|
||||
self.label = app_name.rpartition(".")[2]
|
||||
|
||||
# Root module eg. <module 'django.contrib.admin' from
|
||||
# 'django/contrib/admin/__init__.pyc'>.
|
||||
self.app_module = import_module(app_name)
|
||||
# Human-readable name for the application eg. "Admin".
|
||||
if not hasattr(self, 'verbose_name'):
|
||||
self.verbose_name = self.label.title()
|
||||
|
||||
# Filesystem path to the application directory eg.
|
||||
# u'/usr/lib/python2.7/dist-packages/django/contrib/admin'. May be
|
||||
# None if the application isn't a bona fide package eg. if it's an
|
||||
# egg. Otherwise it's a unicode on Python 2 and a str on Python 3.
|
||||
if not hasattr(self, 'path'):
|
||||
try:
|
||||
self.path = upath(self.app_module.__path__[0])
|
||||
except AttributeError:
|
||||
self.path = None
|
||||
|
||||
# Module containing models eg. <module 'django.contrib.admin.models'
|
||||
# from 'django/contrib/admin/models.pyc'>. Set by import_models().
|
||||
|
@ -34,18 +52,49 @@ class AppConfig(object):
|
|||
# None to prevent accidental access before import_models() runs.
|
||||
self.models = None
|
||||
|
||||
# Filesystem path to the application directory eg.
|
||||
# u'/usr/lib/python2.7/dist-packages/django/contrib/admin'. May be
|
||||
# None if the application isn't a bona fide package eg. if it's an
|
||||
# egg. Otherwise it's a unicode on Python 2 and a str on Python 3.
|
||||
try:
|
||||
self.path = upath(self.app_module.__path__[0])
|
||||
except AttributeError:
|
||||
self.path = None
|
||||
|
||||
def __repr__(self):
|
||||
return '<AppConfig: %s>' % self.label
|
||||
|
||||
@classmethod
|
||||
def create(cls, entry):
|
||||
"""
|
||||
Factory that creates an app config from an entry in INSTALLED_APPS.
|
||||
"""
|
||||
try:
|
||||
# If import_module succeeds, entry is a path to an app module.
|
||||
# Otherwise, entry is a path to an app config class or an error.
|
||||
import_module(entry)
|
||||
|
||||
except ImportError:
|
||||
# Raise the original exception when entry cannot be a path to an
|
||||
# app config class. Since module names are allowable here, the
|
||||
# standard exception message from import_by_path is unsuitable.
|
||||
if '.' not in entry:
|
||||
raise
|
||||
|
||||
cls = import_by_path(entry)
|
||||
|
||||
# Check for obvious errors. (This check prevents duck typing, but
|
||||
# it could be removed if it became a problem in practice.)
|
||||
if not issubclass(cls, AppConfig):
|
||||
raise ImproperlyConfigured(
|
||||
"%r isn't a subclass of AppConfig." % entry)
|
||||
|
||||
# Obtain app name here rather than in AppClass.__init__ to keep
|
||||
# all error checking for entries in INSTALLED_APPS in one place.
|
||||
try:
|
||||
app_name = cls.name
|
||||
except AttributeError:
|
||||
raise ImproperlyConfigured(
|
||||
"%r must supply a name attribute." % entry)
|
||||
|
||||
# Entry is a path to an app config class.
|
||||
return cls(app_name)
|
||||
|
||||
else:
|
||||
# Entry is a path to an app module.
|
||||
return cls(entry)
|
||||
|
||||
def import_models(self, all_models):
|
||||
# Dictionary of models for this app, stored in the 'all_models'
|
||||
# attribute of the AppCache this AppConfig is attached to. Injected as
|
||||
|
|
|
@ -83,7 +83,7 @@ class AppCache(object):
|
|||
# especially not other application modules, even indirectly.
|
||||
# Therefore we simply import them sequentially.
|
||||
for app_name in settings.INSTALLED_APPS:
|
||||
app_config = AppConfig(app_name)
|
||||
app_config = AppConfig.create(app_name)
|
||||
self.app_configs[app_config.label] = app_config
|
||||
|
||||
self._apps_loaded = True
|
||||
|
@ -347,7 +347,7 @@ class AppCache(object):
|
|||
|
||||
def _begin_with_app(self, app_name):
|
||||
# Returns an opaque value that can be passed to _end_with_app().
|
||||
app_config = AppConfig(app_name)
|
||||
app_config = AppConfig.create(app_name)
|
||||
if app_config.label in self.app_configs:
|
||||
return None
|
||||
else:
|
||||
|
@ -412,7 +412,7 @@ class AppCache(object):
|
|||
warnings.warn(
|
||||
"load_app(app_name) is deprecated.",
|
||||
PendingDeprecationWarning, stacklevel=2)
|
||||
app_config = AppConfig(app_name)
|
||||
app_config = AppConfig.create(app_name)
|
||||
app_config.import_models(self.all_models[app_config.label])
|
||||
self.app_configs[app_config.label] = app_config
|
||||
return app_config.models_module
|
||||
|
|
|
@ -226,7 +226,7 @@ class ModulesTestsPackages(IgnoreAllDeprecationWarningsMixin, unittest.TestCase)
|
|||
"Check that the get_tests helper function can find tests in a directory"
|
||||
from django.core.apps.base import AppConfig
|
||||
from django.test.simple import get_tests
|
||||
app_config = AppConfig('test_runner.valid_app')
|
||||
app_config = AppConfig.create('test_runner.valid_app')
|
||||
app_config.import_models({})
|
||||
tests = get_tests(app_config)
|
||||
self.assertIsInstance(tests, types.ModuleType)
|
||||
|
@ -235,7 +235,7 @@ class ModulesTestsPackages(IgnoreAllDeprecationWarningsMixin, unittest.TestCase)
|
|||
"Test for #12658 - Tests with ImportError's shouldn't fail silently"
|
||||
from django.core.apps.base import AppConfig
|
||||
from django.test.simple import get_tests
|
||||
app_config = AppConfig('test_runner_invalid_app')
|
||||
app_config = AppConfig.create('test_runner_invalid_app')
|
||||
app_config.import_models({})
|
||||
with self.assertRaises(ImportError):
|
||||
get_tests(app_config)
|
||||
|
|
Loading…
Reference in New Issue