Fixed #24116 -- Moved AdminSite.check_dependencies() to system checks.

This commit is contained in:
Vincenzo Pandolfo 2015-12-10 12:45:21 +00:00 committed by Tim Graham
parent 956cde8004
commit 0490d72f2a
5 changed files with 93 additions and 39 deletions

View File

@ -1,5 +1,5 @@
from django.apps import AppConfig from django.apps import AppConfig
from django.contrib.admin.checks import check_admin_app from django.contrib.admin.checks import check_admin_app, check_dependencies
from django.core import checks from django.core import checks
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -11,6 +11,7 @@ class SimpleAdminConfig(AppConfig):
verbose_name = _("Administration") verbose_name = _("Administration")
def ready(self): def ready(self):
checks.register(check_dependencies, checks.Tags.admin)
checks.register(check_admin_app, checks.Tags.admin) checks.register(check_admin_app, checks.Tags.admin)

View File

@ -3,6 +3,8 @@ from __future__ import unicode_literals
from itertools import chain from itertools import chain
from django.apps import apps
from django.conf import settings
from django.contrib.admin.utils import ( from django.contrib.admin.utils import (
NotRelationField, flatten, get_fields_from_path, NotRelationField, flatten, get_fields_from_path,
) )
@ -12,6 +14,7 @@ from django.db import models
from django.forms.models import ( from django.forms.models import (
BaseModelForm, BaseModelFormSet, _get_foreign_key, BaseModelForm, BaseModelFormSet, _get_foreign_key,
) )
from django.template.engine import Engine
def check_admin_app(**kwargs): def check_admin_app(**kwargs):
@ -20,6 +23,46 @@ def check_admin_app(**kwargs):
return system_check_errors return system_check_errors
def check_dependencies(**kwargs):
"""
Check that the admin's dependencies are correctly installed.
"""
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
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)
return errors
class BaseModelAdminChecks(object): class BaseModelAdminChecks(object):
def check(self, admin_obj, **kwargs): def check(self, admin_obj, **kwargs):

View File

@ -7,7 +7,6 @@ from django.contrib.auth import REDIRECT_FIELD_NAME
from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.db.models.base import ModelBase from django.db.models.base import ModelBase
from django.http import Http404, HttpResponseRedirect from django.http import Http404, HttpResponseRedirect
from django.template.engine import Engine
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.urls import NoReverseMatch, reverse from django.urls import NoReverseMatch, reverse
from django.utils import six from django.utils import six
@ -172,40 +171,6 @@ class AdminSite(object):
""" """
return request.user.is_active and request.user.is_staff return request.user.is_active and request.user.is_staff
def check_dependencies(self):
"""
Check that all things needed to run the admin have been correctly installed.
The default implementation checks that admin and contenttypes apps are
installed, as well as the auth context processor.
"""
if not apps.is_installed('django.contrib.admin'):
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 your INSTALLED_APPS "
"setting in order to use the admin application.")
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
else:
if ('django.contrib.auth.context_processors.auth'
not in default_template_engine.context_processors):
raise ImproperlyConfigured(
"Enable 'django.contrib.auth.context_processors.auth' "
"in your TEMPLATES setting in order to use the admin "
"application.")
def admin_view(self, view, cacheable=False): def admin_view(self, view, cacheable=False):
""" """
Decorator to create an admin view attached to this ``AdminSite``. This Decorator to create an admin view attached to this ``AdminSite``. This
@ -257,9 +222,6 @@ class AdminSite(object):
# and django.contrib.contenttypes.views imports ContentType. # and django.contrib.contenttypes.views imports ContentType.
from django.contrib.contenttypes import views as contenttype_views from django.contrib.contenttypes import views as contenttype_views
if settings.DEBUG:
self.check_dependencies()
def wrap(view, cacheable=False): def wrap(view, cacheable=False):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
return self.admin_view(view, cacheable)(*args, **kwargs) return self.admin_view(view, cacheable)(*args, **kwargs)

View File

@ -409,6 +409,16 @@ registered as an inline on a :class:`~django.contrib.admin.ModelAdmin`.
* **admin.E304**: ``<model>`` has no ``GenericForeignKey`` using content type * **admin.E304**: ``<model>`` has no ``GenericForeignKey`` using content type
field ``<field name>`` and object ID field ``<field name>``. field ``<field name>`` and object ID field ``<field name>``.
AdminSite
~~~~~~~~~
The following checks are performed on the default
:class:`~django.contrib.admin.AdminSite`:
* **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.
Auth Auth
---- ----

View File

@ -54,6 +54,44 @@ class SystemChecksTestCase(SimpleTestCase):
admin.site.unregister(Song) admin.site.unregister(Song)
admin.sites.system_check_errors = [] admin.sites.system_check_errors = []
@override_settings(INSTALLED_APPS=['django.contrib.admin'])
def test_contenttypes_dependency(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",
)
]
self.assertEqual(errors, expected)
@override_settings(
INSTALLED_APPS=[
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
],
TEMPLATES=[{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [],
},
}],
)
def test_auth_contextprocessor_dependency(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",
)
]
self.assertEqual(errors, expected)
@override_settings(DEBUG=True) @override_settings(DEBUG=True)
def test_custom_adminsite(self): def test_custom_adminsite(self):
class CustomAdminSite(admin.AdminSite): class CustomAdminSite(admin.AdminSite):