diff options
| author | Aymeric Augustin <aymeric.augustin@m4x.org> | 2014-01-24 22:43:00 +0100 |
|---|---|---|
| committer | Aymeric Augustin <aymeric.augustin@m4x.org> | 2014-01-25 10:41:56 +0100 |
| commit | 2ff93e027c7b35378cc450a926bc2e4a446cacf0 (patch) | |
| tree | a6c95d694eadb4bd66b37e9bedad5047d1428d69 /django | |
| parent | 29ddae7436e84f4713c7babeabdf9a1fa62d6543 (diff) | |
Fixed #21829 -- Added default AppConfigs.
Thanks Russell for the report, Marc for the initial patch, Carl for the
final review, and everyone who contributed to the design discussion.
Diffstat (limited to 'django')
24 files changed, 97 insertions, 54 deletions
diff --git a/django/apps/base.py b/django/apps/base.py index ea28797108..2ae40e2454 100644 --- a/django/apps/base.py +++ b/django/apps/base.py @@ -61,13 +61,12 @@ class AppConfig(object): 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. + # If import_module succeeds, entry is a path to an app module, + # which may specify an app config class with default_app_config. # Otherwise, entry is a path to an app config class or an error. module = import_module(entry) except ImportError: - # Avoid django.utils.module_loading.import_by_path because it - # masks errors -- it reraises ImportError as ImproperlyConfigured. mod_path, _, cls_name = entry.rpartition('.') # Raise the original exception when entry cannot be a path to an @@ -75,39 +74,51 @@ class AppConfig(object): if not mod_path: raise - mod = import_module(mod_path) + else: try: - cls = getattr(mod, cls_name) + # If this works, the app module specifies an app config class. + entry = module.default_app_config except AttributeError: - # Emulate the error that "from <mod_path> import <cls_name>" - # would raise when <mod_path> exists but not <cls_name>, with - # more context (Python just says "cannot import name ..."). - raise ImportError( - "cannot import name '%s' from '%s'" % (cls_name, mod_path)) + # Otherwise, it simply uses the default app config class. + return cls(entry, module) + else: + mod_path, _, cls_name = entry.rpartition('.') - # 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( - "'%s' isn't a subclass of AppConfig." % entry) + # If we're reaching this point, we must load the app config class + # located at <mod_path>.<cls_name>. - # 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( - "'%s' must supply a name attribute." % entry) + # Avoid django.utils.module_loading.import_by_path because it + # masks errors -- it reraises ImportError as ImproperlyConfigured. + mod = import_module(mod_path) + try: + cls = getattr(mod, cls_name) + except AttributeError: + # Emulate the error that "from <mod_path> import <cls_name>" + # would raise when <mod_path> exists but not <cls_name>, with + # more context (Python just says "cannot import name ..."). + raise ImportError( + "cannot import name '%s' from '%s'" % (cls_name, mod_path)) - # Ensure app_name points to a valid module. - app_module = import_module(app_name) + # 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( + "'%s' isn't a subclass of AppConfig." % entry) - # Entry is a path to an app config class. - return cls(app_name, app_module) + # 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( + "'%s' must supply a name attribute." % entry) + + # Ensure app_name points to a valid module. + app_module = import_module(app_name) + + # Entry is a path to an app config class. + return cls(app_name, app_module) - else: - # Entry is a path to an app module. - return cls(entry, module) def get_model(self, model_name): """ diff --git a/django/conf/project_template/project_name/settings.py b/django/conf/project_template/project_name/settings.py index 198a3e0d92..efe8091e81 100644 --- a/django/conf/project_template/project_name/settings.py +++ b/django/conf/project_template/project_name/settings.py @@ -30,7 +30,7 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = ( - 'django.contrib.admin.apps.AdminConfig', + 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', diff --git a/django/contrib/admin/__init__.py b/django/contrib/admin/__init__.py index 530badf1df..2218dfd23f 100644 --- a/django/contrib/admin/__init__.py +++ b/django/contrib/admin/__init__.py @@ -1,6 +1,5 @@ # ACTION_CHECKBOX_NAME is unused, but should stay since its import from here # has been referenced in documentation. -from django.contrib.admin.checks import check_admin_app from django.contrib.admin.decorators import register from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME from django.contrib.admin.options import ModelAdmin, HORIZONTAL, VERTICAL @@ -9,7 +8,6 @@ from django.contrib.admin.filters import (ListFilter, SimpleListFilter, FieldListFilter, BooleanFieldListFilter, RelatedFieldListFilter, ChoicesFieldListFilter, DateFieldListFilter, AllValuesFieldListFilter) from django.contrib.admin.sites import AdminSite, site -from django.core import checks from django.utils.module_loading import autodiscover_modules __all__ = [ @@ -24,4 +22,5 @@ __all__ = [ def autodiscover(): autodiscover_modules('admin', register_to=site) -checks.register('admin')(check_admin_app) + +default_app_config = 'django.contrib.admin.apps.AdminConfig' diff --git a/django/contrib/admin/apps.py b/django/contrib/admin/apps.py index a8f0e91ea6..93dab8a787 100644 --- a/django/contrib/admin/apps.py +++ b/django/contrib/admin/apps.py @@ -1,11 +1,22 @@ from django.apps import AppConfig - +from django.core import checks +from django.contrib.admin.checks import check_admin_app from django.utils.translation import ugettext_lazy as _ -class AdminConfig(AppConfig): +class SimpleAdminConfig(AppConfig): + """Simple AppConfig which does not do automatic discovery.""" + name = 'django.contrib.admin' verbose_name = _("administration") def ready(self): + checks.register('admin')(check_admin_app) + + +class AdminConfig(SimpleAdminConfig): + """The default AppConfig for admin which does autodiscovery.""" + + def ready(self): + super(AdminConfig, self).ready() self.module.autodiscover() diff --git a/django/contrib/admin/sites.py b/django/contrib/admin/sites.py index ba7c38dcad..f25055baf7 100644 --- a/django/contrib/admin/sites.py +++ b/django/contrib/admin/sites.py @@ -161,7 +161,7 @@ class AdminSite(object): installed, as well as the auth context processor. """ if not apps.is_installed('django.contrib.admin'): - raise ImproperlyConfigured("Put 'django.contrib.admin.apps.AdminConfig' in " + raise ImproperlyConfigured("Put 'django.contrib.admin' in " "your INSTALLED_APPS setting in order to use the admin application.") if not apps.is_installed('django.contrib.contenttypes'): raise ImproperlyConfigured("Put 'django.contrib.contenttypes' in " diff --git a/django/contrib/admindocs/__init__.py b/django/contrib/admindocs/__init__.py index e69de29bb2..733519ac54 100644 --- a/django/contrib/admindocs/__init__.py +++ b/django/contrib/admindocs/__init__.py @@ -0,0 +1 @@ +default_app_config = 'django.contrib.admindocs.apps.AdminDocsConfig' diff --git a/django/contrib/auth/__init__.py b/django/contrib/auth/__init__.py index 24c870dafe..77b2482e31 100644 --- a/django/contrib/auth/__init__.py +++ b/django/contrib/auth/__init__.py @@ -2,8 +2,6 @@ import inspect import re from django.conf import settings -from django.contrib.auth.checks import check_user_model -from django.core import checks from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.utils.module_loading import import_by_path from django.middleware.csrf import rotate_token @@ -15,10 +13,6 @@ BACKEND_SESSION_KEY = '_auth_user_backend' REDIRECT_FIELD_NAME = 'next' -# Register the user model checks -checks.register('models')(check_user_model) - - def load_backend(path): return import_by_path(path)() @@ -164,3 +158,6 @@ def get_permission_codename(action, opts): Returns the codename of the permission for the specified action. """ return '%s_%s' % (action, opts.model_name) + + +default_app_config = 'django.contrib.auth.apps.AuthConfig' diff --git a/django/contrib/auth/apps.py b/django/contrib/auth/apps.py index e44ad1a0c7..a0389acabe 100644 --- a/django/contrib/auth/apps.py +++ b/django/contrib/auth/apps.py @@ -1,4 +1,6 @@ from django.apps import AppConfig +from django.core import checks +from django.contrib.auth.checks import check_user_model from django.utils.translation import ugettext_lazy as _ @@ -6,3 +8,6 @@ from django.utils.translation import ugettext_lazy as _ class AuthConfig(AppConfig): name = 'django.contrib.auth' verbose_name = _("authentication and authorization") + + def ready(self): + checks.register('models')(check_user_model) diff --git a/django/contrib/comments/__init__.py b/django/contrib/comments/__init__.py index 19dd7ef9a1..a62fff1b56 100644 --- a/django/contrib/comments/__init__.py +++ b/django/contrib/comments/__init__.py @@ -1,6 +1,6 @@ from importlib import import_module import warnings -from django.apps import apps +from django.apps import apps as django_apps from django.conf import settings from django.core import urlresolvers from django.core.exceptions import ImproperlyConfigured @@ -16,7 +16,7 @@ def get_comment_app(): Get the comment app (i.e. "django.contrib.comments") as defined in the settings """ try: - app_config = apps.get_app_config(get_comment_app_name().rpartition(".")[2]) + app_config = django_apps.get_app_config(get_comment_app_name().rpartition(".")[2]) except LookupError: raise ImproperlyConfigured("The COMMENTS_APP (%r) " "must be in INSTALLED_APPS" % settings.COMMENTS_APP) @@ -85,3 +85,6 @@ def get_approve_url(comment): else: return urlresolvers.reverse("django.contrib.comments.views.moderation.approve", args=(comment.id,)) + + +default_app_config = 'django.contrib.comments.apps.CommentsConfig' diff --git a/django/contrib/contenttypes/__init__.py b/django/contrib/contenttypes/__init__.py index 5a04849f06..9789f36eff 100644 --- a/django/contrib/contenttypes/__init__.py +++ b/django/contrib/contenttypes/__init__.py @@ -1,8 +1 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.contrib.contenttypes.checks import check_generic_foreign_keys -from django.core import checks - - -checks.register('models')(check_generic_foreign_keys) +default_app_config = 'django.contrib.contenttypes.apps.ContentTypesConfig' diff --git a/django/contrib/contenttypes/apps.py b/django/contrib/contenttypes/apps.py index 0e55e8d66d..4e515d8a00 100644 --- a/django/contrib/contenttypes/apps.py +++ b/django/contrib/contenttypes/apps.py @@ -1,8 +1,12 @@ from django.apps import AppConfig - +from django.contrib.contenttypes.checks import check_generic_foreign_keys +from django.core import checks from django.utils.translation import ugettext_lazy as _ class ContentTypesConfig(AppConfig): name = 'django.contrib.contenttypes' verbose_name = _("content types") + + def ready(self): + checks.register('models')(check_generic_foreign_keys) diff --git a/django/contrib/flatpages/__init__.py b/django/contrib/flatpages/__init__.py index e69de29bb2..865acd855b 100644 --- a/django/contrib/flatpages/__init__.py +++ b/django/contrib/flatpages/__init__.py @@ -0,0 +1 @@ +default_app_config = 'django.contrib.flatpages.apps.FlatPagesConfig' diff --git a/django/contrib/formtools/__init__.py b/django/contrib/formtools/__init__.py index e69de29bb2..b82588d8e7 100644 --- a/django/contrib/formtools/__init__.py +++ b/django/contrib/formtools/__init__.py @@ -0,0 +1 @@ +default_app_config = 'django.contrib.formtools.apps.FormToolsConfig' diff --git a/django/contrib/gis/__init__.py b/django/contrib/gis/__init__.py index c996fdfdc2..84b3552ce7 100644 --- a/django/contrib/gis/__init__.py +++ b/django/contrib/gis/__init__.py @@ -4,3 +4,6 @@ if six.PY3: memoryview = memoryview else: memoryview = buffer + + +default_app_config = 'django.contrib.gis.apps.GISConfig' diff --git a/django/contrib/humanize/__init__.py b/django/contrib/humanize/__init__.py index e69de29bb2..9ca1642fae 100644 --- a/django/contrib/humanize/__init__.py +++ b/django/contrib/humanize/__init__.py @@ -0,0 +1 @@ +default_app_config = 'django.contrib.humanize.apps.HumanizeConfig' diff --git a/django/contrib/messages/__init__.py b/django/contrib/messages/__init__.py index 40ba6ea6c7..a0cb24b2d9 100644 --- a/django/contrib/messages/__init__.py +++ b/django/contrib/messages/__init__.py @@ -1,2 +1,5 @@ from django.contrib.messages.api import * # NOQA from django.contrib.messages.constants import * # NOQA + + +default_app_config = 'django.contrib.messages.apps.MessagesConfig' diff --git a/django/contrib/redirects/__init__.py b/django/contrib/redirects/__init__.py index e69de29bb2..65364a440b 100644 --- a/django/contrib/redirects/__init__.py +++ b/django/contrib/redirects/__init__.py @@ -0,0 +1 @@ +default_app_config = 'django.contrib.redirects.apps.RedirectsConfig' diff --git a/django/contrib/sessions/__init__.py b/django/contrib/sessions/__init__.py index e69de29bb2..0382f95d7e 100644 --- a/django/contrib/sessions/__init__.py +++ b/django/contrib/sessions/__init__.py @@ -0,0 +1 @@ +default_app_config = 'django.contrib.sessions.apps.SessionsConfig' diff --git a/django/contrib/sitemaps/__init__.py b/django/contrib/sitemaps/__init__.py index 4396708098..ee3d22090d 100644 --- a/django/contrib/sitemaps/__init__.py +++ b/django/contrib/sitemaps/__init__.py @@ -133,3 +133,6 @@ class GenericSitemap(Sitemap): if self.date_field is not None: return getattr(item, self.date_field) return None + + +default_app_config = 'django.contrib.sitemaps.apps.SiteMapsConfig' diff --git a/django/contrib/sites/__init__.py b/django/contrib/sites/__init__.py index e69de29bb2..b554240bb2 100644 --- a/django/contrib/sites/__init__.py +++ b/django/contrib/sites/__init__.py @@ -0,0 +1 @@ +default_app_config = 'django.contrib.sites.apps.SitesConfig' diff --git a/django/contrib/staticfiles/__init__.py b/django/contrib/staticfiles/__init__.py index e69de29bb2..8a07b83d2f 100644 --- a/django/contrib/staticfiles/__init__.py +++ b/django/contrib/staticfiles/__init__.py @@ -0,0 +1 @@ +default_app_config = 'django.contrib.staticfiles.apps.StaticFilesConfig' diff --git a/django/contrib/syndication/__init__.py b/django/contrib/syndication/__init__.py index e69de29bb2..2c3f52578d 100644 --- a/django/contrib/syndication/__init__.py +++ b/django/contrib/syndication/__init__.py @@ -0,0 +1 @@ +default_app_config = 'django.contrib.syndication.apps.SyndicationConfig' diff --git a/django/contrib/webdesign/__init__.py b/django/contrib/webdesign/__init__.py index e69de29bb2..2340116bc8 100644 --- a/django/contrib/webdesign/__init__.py +++ b/django/contrib/webdesign/__init__.py @@ -0,0 +1 @@ +default_app_config = 'django.contrib.webdesign.apps.WebDesignConfig' diff --git a/django/core/checks/registry.py b/django/core/checks/registry.py index efb5b4b892..21efbf317e 100644 --- a/django/core/checks/registry.py +++ b/django/core/checks/registry.py @@ -29,7 +29,8 @@ class CheckRegistry(object): def inner(check): check.tags = tags - self.registered_checks.append(check) + if check not in self.registered_checks: + self.registered_checks.append(check) return check return inner |
