Skip to content

Commit

Permalink
Fixed #29695 -- Added system checks for admin's app dependencies and …
Browse files Browse the repository at this point in the history
…TEMPLATES setting.
  • Loading branch information
math-a3k authored and timgraham committed Aug 20, 2018
1 parent 49b6793 commit 68e81a0
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 38 deletions.
91 changes: 61 additions & 30 deletions django/contrib/admin/checks.py
Expand Up @@ -14,7 +14,8 @@
from django.forms.models import (
BaseModelForm, BaseModelFormSet, _get_foreign_key,
)
from django.template.engine import Engine
from django.template import engines
from django.template.backends.django import DjangoTemplates
from django.utils.deprecation import RemovedInDjango30Warning
from django.utils.inspect import get_func_args

Expand All @@ -31,38 +32,68 @@ def check_dependencies(**kwargs):
"""
Check that the admin's dependencies are correctly installed.
"""
if not apps.is_installed('django.contrib.admin'):
return []
errors = []
# contrib.contenttypes must be installed.
if not apps.is_installed('django.contrib.contenttypes'):
missing_app = checks.Error(
"'django.contrib.contenttypes' must be in INSTALLED_APPS in order "
"to use the admin application.",
id="admin.E401",
)
errors.append(missing_app)
# The auth context processor must be installed if using the default
# authentication backend.
try:
default_template_engine = Engine.get_default()
except Exception:
# Skip this non-critical check:
# 1. if the user has a non-trivial TEMPLATES setting and Django
# can't find a default template engine
# 2. if anything goes wrong while loading template engines, in
# order to avoid raising an exception from a confusing location
# Catching ImproperlyConfigured suffices for 1. but 2. requires
# catching all exceptions.
pass
app_dependencies = (
('django.contrib.contenttypes', 401),
('django.contrib.auth', 405),
('django.contrib.messages', 406),
('django.contrib.sessions', 407),
)
for app_name, error_code in app_dependencies:
if not apps.is_installed(app_name):
errors.append(checks.Error(
"'%s' must be in INSTALLED_APPS in order to use the admin "
"application." % app_name,
id='admin.E%d' % error_code,
))
for engine in engines.all():
if isinstance(engine, DjangoTemplates):
django_templates_instance = engine.engine
break
else:
django_templates_instance = None
if not django_templates_instance:
errors.append(checks.Error(
"A 'django.template.backends.django.DjangoTemplates' instance "
"must be configured in TEMPLATES in order to use the admin "
"application.",
id='admin.E403',
))
else:
if ('django.contrib.auth.context_processors.auth'
not in default_template_engine.context_processors and
'django.contrib.auth.backends.ModelBackend' in settings.AUTHENTICATION_BACKENDS):
missing_template = checks.Error(
"'django.contrib.auth.context_processors.auth' must be in "
"TEMPLATES in order to use the admin application.",
id="admin.E402"
)
errors.append(missing_template)
not in django_templates_instance.context_processors and
'django.contrib.auth.backends.ModelBackend'
in settings.AUTHENTICATION_BACKENDS):
errors.append(checks.Error(
"'django.contrib.auth.context_processors.auth' must be "
"enabled in DjangoTemplates (TEMPLATES) if using the default "
"auth backend in order to use the admin application.",
id='admin.E402',
))
if ('django.contrib.messages.context_processors.messages'
not in django_templates_instance.context_processors):
errors.append(checks.Error(
"'django.contrib.messages.context_processors.messages' must "
"be enabled in DjangoTemplates (TEMPLATES) in order to use "
"the admin application.",
id='admin.E404',
))
if ('django.contrib.auth.middleware.AuthenticationMiddleware'
not in settings.MIDDLEWARE):
errors.append(checks.Error(
"'django.contrib.auth.middleware.AuthenticationMiddleware' must "
"be in MIDDLEWARE in order to use the admin application.",
id='admin.E408',
))
if ('django.contrib.messages.middleware.MessageMiddleware'
not in settings.MIDDLEWARE):
errors.append(checks.Error(
"'django.contrib.messages.middleware.MessageMiddleware' must "
"be in MIDDLEWARE in order to use the admin application.",
id='admin.E409',
))
return errors


Expand Down
21 changes: 20 additions & 1 deletion docs/ref/checks.txt
Expand Up @@ -624,7 +624,26 @@ The following checks are performed on the default
* **admin.E401**: :mod:`django.contrib.contenttypes` must be in
:setting:`INSTALLED_APPS` in order to use the admin application.
* **admin.E402**: :mod:`django.contrib.auth.context_processors.auth`
must be in :setting:`TEMPLATES` in order to use the admin application.
must be enabled in :class:`~django.template.backends.django.DjangoTemplates`
(:setting:`TEMPLATES`) if using the default auth backend in order to use the
admin application.
* **admin.E403**: A :class:`django.template.backends.django.DjangoTemplates`
instance must be configured in :setting:`TEMPLATES` in order to use the
admin application.
* **admin.E404**: ``django.contrib.messages.context_processors.messages``
must be enabled in :class:`~django.template.backends.django.DjangoTemplates`
(:setting:`TEMPLATES`) in order to use the admin application.
* **admin.E405**: :mod:`django.contrib.auth` must be in
:setting:`INSTALLED_APPS` in order to use the admin application.
* **admin.E406**: :mod:`django.contrib.messages` must be in
:setting:`INSTALLED_APPS` in order to use the admin application.
* **admin.E407**: :mod:`django.contrib.sessions` must be in
:setting:`INSTALLED_APPS` in order to use the admin application.
* **admin.E408**:
:class:`django.contrib.auth.middleware.AuthenticationMiddleware` must be in
:setting:`MIDDLEWARE` in order to use the admin application.
* **admin.E409**: :class:`django.contrib.messages.middleware.MessageMiddleware`
must be in :setting:`MIDDLEWARE` in order to use the admin application.

``auth``
--------
Expand Down
87 changes: 81 additions & 6 deletions tests/admin_checks/tests.py
Expand Up @@ -43,6 +43,8 @@ def check(self, **kwargs):
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'admin_checks',
],
)
Expand All @@ -58,20 +60,42 @@ def test_checks_are_performed(self):
admin.site.unregister(Song)

@override_settings(INSTALLED_APPS=['django.contrib.admin'])
def test_contenttypes_dependency(self):
def test_apps_dependencies(self):
errors = admin.checks.check_dependencies()
expected = [
checks.Error(
"'django.contrib.contenttypes' must be in "
"INSTALLED_APPS in order to use the admin application.",
id="admin.E401",
),
checks.Error(
"'django.contrib.auth' must be in INSTALLED_APPS in order "
"to use the admin application.",
id='admin.E405',
),
checks.Error(
"'django.contrib.messages' must be in INSTALLED_APPS in order "
"to use the admin application.",
id='admin.E406',
),
checks.Error(
"'django.contrib.sessions' must be in INSTALLED_APPS in order "
"to use the admin application.",
id='admin.E407',
)
]
self.assertEqual(errors, expected)

@override_settings(TEMPLATES=[])
def test_no_template_engines(self):
self.assertEqual(admin.checks.check_dependencies(), [])
self.assertEqual(admin.checks.check_dependencies(), [
checks.Error(
"A 'django.template.backends.django.DjangoTemplates' "
"instance must be configured in TEMPLATES in order to use "
"the admin application.",
id='admin.E403',
)
])

@override_settings(
TEMPLATES=[{
Expand All @@ -83,13 +107,64 @@ def test_no_template_engines(self):
},
}],
)
def test_auth_contextprocessor_dependency(self):
def test_context_processor_dependencies(self):
expected = [
checks.Error(
"'django.contrib.auth.context_processors.auth' must be "
"enabled in DjangoTemplates (TEMPLATES) if using the default "
"auth backend in order to use the admin application.",
id='admin.E402',
),
checks.Error(
"'django.contrib.messages.context_processors.messages' must "
"be enabled in DjangoTemplates (TEMPLATES) in order to use "
"the admin application.",
id='admin.E404',
)
]
self.assertEqual(admin.checks.check_dependencies(), expected)
# The first error doesn't happen if
# 'django.contrib.auth.backends.ModelBackend' isn't in
# AUTHENTICATION_BACKENDS.
with self.settings(AUTHENTICATION_BACKENDS=[]):
self.assertEqual(admin.checks.check_dependencies(), expected[1:])

@override_settings(
TEMPLATES=[
{
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'DIRS': [],
'APP_DIRS': True,
},
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
],
)
def test_several_templates_backends(self):
self.assertEqual(admin.checks.check_dependencies(), [])

@override_settings(MIDDLEWARE=[])
def test_middleware_dependencies(self):
errors = admin.checks.check_dependencies()
expected = [
checks.Error(
"'django.contrib.auth.context_processors.auth' must be in "
"TEMPLATES in order to use the admin application.",
id="admin.E402",
"'django.contrib.auth.middleware.AuthenticationMiddleware' "
"must be in MIDDLEWARE in order to use the admin application.",
id='admin.E408',
),
checks.Error(
"'django.contrib.messages.middleware.MessageMiddleware' "
"must be in MIDDLEWARE in order to use the admin application.",
id='admin.E409',
)
]
self.assertEqual(errors, expected)
Expand Down
21 changes: 20 additions & 1 deletion tests/admin_scripts/tests.py
Expand Up @@ -1172,9 +1172,28 @@ def test_complex_app(self):
'django.contrib.admin.apps.SimpleAdminConfig',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.messages',
'django.contrib.sessions',
],
sdict={
'DEBUG': True
'DEBUG': True,
'MIDDLEWARE': [
'django.contrib.messages.middleware.MessageMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
],
'TEMPLATES': [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
],
}
)
args = ['check']
Expand Down

0 comments on commit 68e81a0

Please sign in to comment.