diff --git a/django/views/debug.py b/django/views/debug.py
index 5a1b4aee91a0..f7e141d1c680 100644
--- a/django/views/debug.py
+++ b/django/views/debug.py
@@ -11,7 +11,7 @@
from django.http import Http404, HttpResponse, HttpResponseNotFound
from django.template import Context, Engine, TemplateDoesNotExist
from django.template.defaultfilters import pprint
-from django.urls import resolve
+from django.urls import URLResolver, resolve
from django.utils import timezone
from django.utils.datastructures import MultiValueDict
from django.utils.encoding import force_str
@@ -635,6 +635,20 @@ def technical_404_response(request, exception):
):
return default_urlconf(request)
+ patterns_with_debug_info = []
+ for urlpattern in tried or ():
+ patterns = []
+ for inner_pattern in urlpattern:
+ wrapper = {"tried": inner_pattern}
+ if isinstance(inner_pattern, URLResolver):
+ wrapper["debug_key"] = "namespace"
+ wrapper["debug_val"] = inner_pattern.namespace
+ else:
+ wrapper["debug_key"] = "name"
+ wrapper["debug_val"] = inner_pattern.name
+ patterns.append(wrapper)
+ patterns_with_debug_info.append(patterns)
+
urlconf = getattr(request, "urlconf", settings.ROOT_URLCONF)
if isinstance(urlconf, types.ModuleType):
urlconf = urlconf.__name__
@@ -647,7 +661,8 @@ def technical_404_response(request, exception):
"urlconf": urlconf,
"root_urlconf": settings.ROOT_URLCONF,
"request_path": error_url,
- "urlpatterns": tried,
+ "urlpatterns": tried, # Unused, left for compatibility.
+ "urlpatterns_debug": patterns_with_debug_info,
"resolved": resolved,
"reason": str(exception),
"request": request,
diff --git a/django/views/templates/technical_404.html b/django/views/templates/technical_404.html
index f8d4e92c0843..73abb22af465 100644
--- a/django/views/templates/technical_404.html
+++ b/django/views/templates/technical_404.html
@@ -46,18 +46,18 @@
Page not found (404)
- {% if urlpatterns %}
+ {% if urlpatterns_debug %}
Using the URLconf defined in {{ urlconf }},
Django tried these URL patterns, in this order:
- {% for pattern in urlpatterns %}
+ {% for pattern in urlpatterns_debug %}
-
{% for pat in pattern %}
- {{ pat.pattern }}
- {% if forloop.last and pat.name %}[name='{{ pat.name }}']{% endif %}
+ {{ pat.tried.pattern }}
+ {% if forloop.last and pat.debug_val %}[{{ pat.debug_key }}='{{ pat.debug_val }}']{% endif %}
{% endfor %}
diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py
index 8e36ab7eb1ee..439faff84eba 100644
--- a/tests/view_tests/tests/test_debug.py
+++ b/tests/view_tests/tests/test_debug.py
@@ -423,6 +423,16 @@ def test_default_urlconf_script_name(self):
response, "The install worked successfully! Congratulations!
"
)
+ @override_settings(ROOT_URLCONF="view_tests.default_urls")
+ def test_default_urlconf_technical_404(self):
+ response = self.client.get("/favicon.ico")
+ self.assertContains(
+ response,
+ "\nadmin/\n[namespace='admin']\n",
+ status_code=404,
+ html=True,
+ )
+
@override_settings(ROOT_URLCONF="view_tests.regression_21530_urls")
def test_regression_21530(self):
"""