From 371ece2f0682e51f2f796854d3e091827a7cea63 Mon Sep 17 00:00:00 2001 From: Rodrigo Date: Mon, 20 Aug 2018 18:57:46 -0300 Subject: [PATCH] Fixed #29695 -- Added system checks for admin's app dependencies and TEMPLATES setting. --- django/contrib/admin/checks.py | 91 +++++++++++++++++++++++----------- docs/ref/checks.txt | 21 +++++++- tests/admin_checks/tests.py | 87 +++++++++++++++++++++++++++++--- tests/admin_scripts/tests.py | 21 +++++++- 4 files changed, 182 insertions(+), 38 deletions(-) diff --git a/django/contrib/admin/checks.py b/django/contrib/admin/checks.py index fbc8d50693..d129acb124 100644 --- a/django/contrib/admin/checks.py +++ b/django/contrib/admin/checks.py @@ -14,7 +14,8 @@ from django.db.models.expressions import Combinable, F, OrderBy 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 @@ -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 diff --git a/docs/ref/checks.txt b/docs/ref/checks.txt index ee16ebde54..a84d9b60b6 100644 --- a/docs/ref/checks.txt +++ b/docs/ref/checks.txt @@ -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`` -------- diff --git a/tests/admin_checks/tests.py b/tests/admin_checks/tests.py index a968cd5ba3..d5661a137d 100644 --- a/tests/admin_checks/tests.py +++ b/tests/admin_checks/tests.py @@ -43,6 +43,8 @@ class MyAdmin(admin.ModelAdmin): 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', 'admin_checks', ], ) @@ -58,20 +60,42 @@ class SystemChecksTestCase(SimpleTestCase): 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=[{ @@ -83,13 +107,64 @@ class SystemChecksTestCase(SimpleTestCase): }, }], ) - 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) diff --git a/tests/admin_scripts/tests.py b/tests/admin_scripts/tests.py index c1d8baef6b..c60194fe72 100644 --- a/tests/admin_scripts/tests.py +++ b/tests/admin_scripts/tests.py @@ -1172,9 +1172,28 @@ class ManageCheck(AdminScriptTestCase): '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']