Skip to content

Commit

Permalink
Fixed #30237 -- Changed admin checks to accept subclasses
Browse files Browse the repository at this point in the history
  • Loading branch information
hermansc committed Mar 11, 2019
1 parent 4c086d7 commit 20801d9
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 13 deletions.
31 changes: 23 additions & 8 deletions django/contrib/admin/checks.py
Expand Up @@ -15,6 +15,7 @@
)
from django.template import engines
from django.template.backends.django import DjangoTemplates
from django.utils.module_loading import import_string


def _issubclass(cls, classinfo):
Expand All @@ -28,6 +29,22 @@ def _issubclass(cls, classinfo):
return False


def _contains_subclass(subclass_path, candidate_paths):
"""
Check if dotted subclass module path is found among a list of candidate
paths.
"""
subclass = import_string(subclass_path)
for path in candidate_paths:
try:
candidate_cls = import_string(path)
except ImportError:
continue
if _issubclass(candidate_cls, subclass):
return True
return False


def check_admin_app(app_configs, **kwargs):
from django.contrib.admin.sites import all_sites
errors = []
Expand Down Expand Up @@ -72,8 +89,7 @@ def check_dependencies(**kwargs):
else:
if ('django.contrib.auth.context_processors.auth'
not in django_templates_instance.context_processors and
'django.contrib.auth.backends.ModelBackend'
in settings.AUTHENTICATION_BACKENDS):
_contains_subclass('django.contrib.auth.backends.ModelBackend', settings.AUTHENTICATION_BACKENDS)):
errors.append(checks.Error(
"'django.contrib.auth.context_processors.auth' must be "
"enabled in DjangoTemplates (TEMPLATES) if using the default "
Expand All @@ -88,17 +104,16 @@ def check_dependencies(**kwargs):
"the admin application.",
id='admin.E404',
))
if ('django.contrib.auth.middleware.AuthenticationMiddleware'
not in settings.MIDDLEWARE):

if not _contains_subclass('django.contrib.auth.middleware.AuthenticationMiddleware', settings.MIDDLEWARE):
errors.append(checks.Error(
"'django.contrib.auth.middleware.AuthenticationMiddleware' must "
"'django.contrib.auth.middleware.AuthenticationMiddleware' (or a subclass) must "
"be in MIDDLEWARE in order to use the admin application.",
id='admin.E408',
))
if ('django.contrib.messages.middleware.MessageMiddleware'
not in settings.MIDDLEWARE):
if not _contains_subclass('django.contrib.messages.middleware.MessageMiddleware', settings.MIDDLEWARE):
errors.append(checks.Error(
"'django.contrib.messages.middleware.MessageMiddleware' must "
"'django.contrib.messages.middleware.MessageMiddleware' (or a subclass) must "
"be in MIDDLEWARE in order to use the admin application.",
id='admin.E409',
))
Expand Down
8 changes: 5 additions & 3 deletions docs/ref/checks.txt
Expand Up @@ -658,10 +658,12 @@ The following checks are performed on the default
* **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.
:class:`django.contrib.auth.middleware.AuthenticationMiddleware` (or a
subclass) 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.
(or a subclass) must be in :setting:`MIDDLEWARE` in order to use the admin
application.

``auth``
--------
Expand Down
19 changes: 17 additions & 2 deletions tests/admin_checks/tests.py
@@ -1,6 +1,8 @@
from django import forms
from django.contrib import admin
from django.contrib.admin import AdminSite
from django.contrib.auth.middleware import AuthenticationMiddleware
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.contenttypes.admin import GenericStackedInline
from django.core import checks
from django.test import SimpleTestCase, override_settings
Expand Down Expand Up @@ -37,6 +39,14 @@ def check(self, **kwargs):
return ['error!']


class MyAuthMiddleware(AuthenticationMiddleware):
pass


class MyMessageMiddleware(MessageMiddleware):
pass


@override_settings(
SILENCED_SYSTEM_CHECKS=['fields.W342'], # ForeignKey(unique=True)
INSTALLED_APPS=[
Expand Down Expand Up @@ -157,18 +167,23 @@ def test_middleware_dependencies(self):
errors = admin.checks.check_dependencies()
expected = [
checks.Error(
"'django.contrib.auth.middleware.AuthenticationMiddleware' "
"'django.contrib.auth.middleware.AuthenticationMiddleware' (or a subclass) "
"must be in MIDDLEWARE in order to use the admin application.",
id='admin.E408',
),
checks.Error(
"'django.contrib.messages.middleware.MessageMiddleware' "
"'django.contrib.messages.middleware.MessageMiddleware' (or a subclass) "
"must be in MIDDLEWARE in order to use the admin application.",
id='admin.E409',
)
]
self.assertEqual(errors, expected)

# Subclasses are permitted
with override_settings(MIDDLEWARE=[
'admin_checks.tests.MyAuthMiddleware', 'admin_checks.tests.MyMessageMiddleware']):
self.assertEqual(admin.checks.check_dependencies(), [])

def test_custom_adminsite(self):
class CustomAdminSite(admin.AdminSite):
pass
Expand Down

0 comments on commit 20801d9

Please sign in to comment.