Skip to content

Commit

Permalink
Check if module path is superclass of required classes for admin
Browse files Browse the repository at this point in the history
  • Loading branch information
hermansc committed Mar 11, 2019
1 parent 4c086d7 commit 331afb6
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 12 deletions.
34 changes: 24 additions & 10 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 @@ -70,10 +87,8 @@ def check_dependencies(**kwargs):
id='admin.E403',
))
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):
if ('django.contrib.auth.context_processors.auth' not in django_templates_instance.context_processors and
_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 +103,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
16 changes: 14 additions & 2 deletions tests/admin_checks/tests.py
@@ -1,6 +1,7 @@
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.contenttypes.admin import GenericStackedInline
from django.core import checks
from django.test import SimpleTestCase, override_settings
Expand Down Expand Up @@ -37,6 +38,10 @@ def check(self, **kwargs):
return ['error!']


class MyAuthMiddleware(AuthenticationMiddleware):
pass


@override_settings(
SILENCED_SYSTEM_CHECKS=['fields.W342'], # ForeignKey(unique=True)
INSTALLED_APPS=[
Expand Down Expand Up @@ -157,18 +162,25 @@ 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)

@override_settings(MIDDLEWARE=[
'admin_checks.tests.MyAuthMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
])
def test_subclass_middleware_dependencies(self):
self.assertEqual(admin.checks.check_dependencies(), [])

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

0 comments on commit 331afb6

Please sign in to comment.