Fixed #27612 -- Added a check for duplicate URL instance namespaces.
This commit is contained in:
parent
3188b49ee2
commit
24fa728a47
|
@ -1,9 +1,11 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from collections import Counter
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import six
|
||||
|
||||
from . import Error, Tags, register
|
||||
from . import Error, Tags, Warning, register
|
||||
|
||||
|
||||
@register(Tags.urls)
|
||||
|
@ -28,6 +30,43 @@ def check_resolver(resolver):
|
|||
return []
|
||||
|
||||
|
||||
@register(Tags.urls)
|
||||
def check_url_namespaces_unique(app_configs, **kwargs):
|
||||
"""
|
||||
Warn if URL namespaces used in applications aren't unique.
|
||||
"""
|
||||
if not getattr(settings, 'ROOT_URLCONF', None):
|
||||
return []
|
||||
|
||||
from django.urls import get_resolver
|
||||
resolver = get_resolver()
|
||||
all_namespaces = _load_all_namespaces(resolver)
|
||||
counter = Counter(all_namespaces)
|
||||
non_unique_namespaces = [n for n, count in counter.items() if count > 1]
|
||||
errors = []
|
||||
for namespace in non_unique_namespaces:
|
||||
errors.append(Warning(
|
||||
"URL namespace '{}' isn't unique. You may not be able to reverse "
|
||||
"all URLs in this namespace".format(namespace),
|
||||
id="urls.W005",
|
||||
))
|
||||
return errors
|
||||
|
||||
|
||||
def _load_all_namespaces(resolver):
|
||||
"""
|
||||
Recursively load all namespaces from URL patterns.
|
||||
"""
|
||||
url_patterns = getattr(resolver, 'url_patterns', [])
|
||||
namespaces = [
|
||||
url.namespace for url in url_patterns
|
||||
if getattr(url, 'namespace', None) is not None
|
||||
]
|
||||
for pattern in url_patterns:
|
||||
namespaces.extend(_load_all_namespaces(pattern))
|
||||
return namespaces
|
||||
|
||||
|
||||
def get_warning_for_invalid_pattern(pattern):
|
||||
"""
|
||||
Return a list containing a warning that the pattern is invalid.
|
||||
|
|
|
@ -673,3 +673,5 @@ The following checks are performed on your URL configuration:
|
|||
references.
|
||||
* **urls.E004**: Your URL pattern ``<pattern>`` is invalid. Ensure that
|
||||
``urlpatterns`` is a list of :func:`~django.conf.urls.url()` instances.
|
||||
* **urls.W005**: URL namespace ``<namespace>`` isn't unique. You may not be
|
||||
able to reverse all URLs in this namespace.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from django.conf import settings
|
||||
from django.core.checks.messages import Warning
|
||||
from django.core.checks.urls import (
|
||||
check_url_config, get_warning_for_invalid_pattern,
|
||||
check_url_config, check_url_namespaces_unique, get_warning_for_invalid_pattern,
|
||||
)
|
||||
from django.test import SimpleTestCase
|
||||
from django.test.utils import override_settings
|
||||
|
@ -94,3 +95,23 @@ class CheckUrlsTest(SimpleTestCase):
|
|||
def test_get_warning_for_invalid_pattern_other(self):
|
||||
warning = get_warning_for_invalid_pattern(object())[0]
|
||||
self.assertIsNone(warning.hint)
|
||||
|
||||
@override_settings(ROOT_URLCONF='check_framework.urls.non_unique_namespaces')
|
||||
def test_check_non_unique_namespaces(self):
|
||||
result = check_url_namespaces_unique(None)
|
||||
self.assertEqual(len(result), 2)
|
||||
non_unique_namespaces = ['app-ns1', 'app-1']
|
||||
warning_messages = [
|
||||
"URL namespace '{}' isn't unique. You may not be able to reverse "
|
||||
"all URLs in this namespace".format(namespace)
|
||||
for namespace in non_unique_namespaces
|
||||
]
|
||||
for warning in result:
|
||||
self.assertIsInstance(warning, Warning)
|
||||
self.assertEqual('urls.W005', warning.id)
|
||||
self.assertIn(warning.msg, warning_messages)
|
||||
|
||||
@override_settings(ROOT_URLCONF='check_framework.urls.unique_namespaces')
|
||||
def test_check_unique_namespaces(self):
|
||||
result = check_url_namespaces_unique(None)
|
||||
self.assertEqual(result, [])
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
from django.conf.urls import include, url
|
||||
|
||||
common_url_patterns = ([
|
||||
url(r'^app-ns1/', include([])),
|
||||
url(r'^app-url/', include([])),
|
||||
], 'app-ns1')
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^app-ns1-0/', include(common_url_patterns)),
|
||||
url(r'^app-ns1-1/', include(common_url_patterns)),
|
||||
url(r'^app-some-url/', include(([], 'app'), namespace='app-1')),
|
||||
url(r'^app-some-url-2/', include(([], 'app'), namespace='app-1'))
|
||||
]
|
|
@ -0,0 +1,11 @@
|
|||
from django.conf.urls import include, url
|
||||
|
||||
common_url_patterns = ([
|
||||
url(r'^app-ns1/', include([])),
|
||||
url(r'^app-url/', include([])),
|
||||
], 'common')
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^app-ns1-0/', include(common_url_patterns, namespace='app-include-1')),
|
||||
url(r'^app-ns1-1/', include(common_url_patterns, namespace='app-include-2'))
|
||||
]
|
Loading…
Reference in New Issue