From 7894776bc99e42f20e0919fc9027e6db542957d5 Mon Sep 17 00:00:00 2001 From: Jacob Walls Date: Thu, 18 Sep 2025 12:53:24 -0400 Subject: [PATCH] Refs #28526 -- Provided URLResolver namespace in technical 404 template. This avoids looking up the nonexistent "name" attribute on URLResolver, which logs verbosely. --- django/views/debug.py | 19 +++++++++++++++++-- django/views/templates/technical_404.html | 8 ++++---- tests/view_tests/tests/test_debug.py | 10 ++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/django/views/debug.py b/django/views/debug.py index 5a1b4aee91..f7e141d1c6 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -11,7 +11,7 @@ from django.conf import settings 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 f8d4e92c08..73abb22af4 100644 --- a/django/views/templates/technical_404.html +++ b/django/views/templates/technical_404.html @@ -46,18 +46,18 @@
- {% 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 %}
  1. {% 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 %}
  2. diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py index 8e36ab7eb1..439faff84e 100644 --- a/tests/view_tests/tests/test_debug.py +++ b/tests/view_tests/tests/test_debug.py @@ -423,6 +423,16 @@ class DebugViewTests(SimpleTestCase): 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): """