Fixed #31791 -- Made technical 404 debug page display the tried URL patterns for Http404.
This commit is contained in:
parent
83dea65ed6
commit
11ebc6479f
|
@ -30,12 +30,13 @@ from .utils import get_callable
|
|||
|
||||
|
||||
class ResolverMatch:
|
||||
def __init__(self, func, args, kwargs, url_name=None, app_names=None, namespaces=None, route=None):
|
||||
def __init__(self, func, args, kwargs, url_name=None, app_names=None, namespaces=None, route=None, tried=None):
|
||||
self.func = func
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.url_name = url_name
|
||||
self.route = route
|
||||
self.tried = tried
|
||||
|
||||
# If a URLRegexResolver doesn't have a namespace or app_name, it passes
|
||||
# in an empty value.
|
||||
|
@ -525,6 +526,13 @@ class URLResolver:
|
|||
self._populate()
|
||||
return self._app_dict[language_code]
|
||||
|
||||
@staticmethod
|
||||
def _extend_tried(tried, pattern, sub_tried=None):
|
||||
if sub_tried is None:
|
||||
tried.append([pattern])
|
||||
else:
|
||||
tried.extend([pattern, *t] for t in sub_tried)
|
||||
|
||||
@staticmethod
|
||||
def _join_route(route1, route2):
|
||||
"""Join two routes, without the starting ^ in the second route."""
|
||||
|
@ -549,11 +557,7 @@ class URLResolver:
|
|||
try:
|
||||
sub_match = pattern.resolve(new_path)
|
||||
except Resolver404 as e:
|
||||
sub_tried = e.args[0].get('tried')
|
||||
if sub_tried is not None:
|
||||
tried.extend([pattern] + t for t in sub_tried)
|
||||
else:
|
||||
tried.append([pattern])
|
||||
self._extend_tried(tried, pattern, e.args[0].get('tried'))
|
||||
else:
|
||||
if sub_match:
|
||||
# Merge captured arguments in match with submatch
|
||||
|
@ -566,6 +570,7 @@ class URLResolver:
|
|||
if not sub_match_dict:
|
||||
sub_match_args = args + sub_match.args
|
||||
current_route = '' if isinstance(pattern, URLPattern) else str(pattern.pattern)
|
||||
self._extend_tried(tried, pattern, sub_match.tried)
|
||||
return ResolverMatch(
|
||||
sub_match.func,
|
||||
sub_match_args,
|
||||
|
@ -574,8 +579,9 @@ class URLResolver:
|
|||
[self.app_name] + sub_match.app_names,
|
||||
[self.namespace] + sub_match.namespaces,
|
||||
self._join_route(current_route, sub_match.route),
|
||||
tried,
|
||||
)
|
||||
tried.append([pattern])
|
||||
self._extend_tried(tried, pattern)
|
||||
raise Resolver404({'tried': tried, 'path': new_path})
|
||||
raise Resolver404({'path': path})
|
||||
|
||||
|
|
|
@ -481,8 +481,10 @@ def technical_404_response(request, exception):
|
|||
try:
|
||||
tried = exception.args[0]['tried']
|
||||
except (IndexError, TypeError, KeyError):
|
||||
tried = []
|
||||
resolved = True
|
||||
tried = request.resolver_match.tried if request.resolver_match else None
|
||||
else:
|
||||
resolved = False
|
||||
if (not tried or ( # empty URLconf
|
||||
request.path == '/' and
|
||||
len(tried) == 1 and # default URLconf
|
||||
|
@ -520,6 +522,7 @@ def technical_404_response(request, exception):
|
|||
'root_urlconf': settings.ROOT_URLCONF,
|
||||
'request_path': error_url,
|
||||
'urlpatterns': tried,
|
||||
'resolved': resolved,
|
||||
'reason': str(exception),
|
||||
'request': request,
|
||||
'settings': reporter_filter.get_safe_settings(),
|
||||
|
|
|
@ -60,8 +60,11 @@
|
|||
</ol>
|
||||
<p>
|
||||
{% if request_path %}
|
||||
The current path, <code>{{ request_path }}</code>,{% else %}
|
||||
The empty path{% endif %} didn’t match any of these.
|
||||
The current path, <code>{{ request_path }}</code>,
|
||||
{% else %}
|
||||
The empty path
|
||||
{% endif %}
|
||||
{% if resolved %}matched the last one.{% else %}didn’t match any of these.{% endif %}
|
||||
</p>
|
||||
{% else %}
|
||||
<p>{{ reason }}</p>
|
||||
|
|
|
@ -137,6 +137,13 @@ If the URL does not resolve, the function raises a
|
|||
For example, if ``path('users/<id>/', ...)`` is the matching pattern,
|
||||
``route`` will contain ``'users/<id>/'``.
|
||||
|
||||
.. attribute:: ResolverMatch.tried
|
||||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
The list of URL patterns tried before the URL either matched one or
|
||||
exhausted available patterns.
|
||||
|
||||
.. attribute:: ResolverMatch.app_name
|
||||
|
||||
The application namespace for the URL pattern that matches the
|
||||
|
|
|
@ -113,13 +113,25 @@ class DebugViewTests(SimpleTestCase):
|
|||
def test_404(self):
|
||||
response = self.client.get('/raises404/')
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertContains(response, '<code>not-in-urls</code>, didn’t match', status_code=404)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<p>The current path, <code>not-in-urls</code>, didn’t match any '
|
||||
'of these.</p>',
|
||||
status_code=404,
|
||||
html=True,
|
||||
)
|
||||
|
||||
def test_404_not_in_urls(self):
|
||||
response = self.client.get('/not-in-urls')
|
||||
self.assertNotContains(response, "Raised by:", status_code=404)
|
||||
self.assertContains(response, "Django tried these URL patterns", status_code=404)
|
||||
self.assertContains(response, '<code>not-in-urls</code>, didn’t match', status_code=404)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<p>The current path, <code>not-in-urls</code>, didn’t match any '
|
||||
'of these.</p>',
|
||||
status_code=404,
|
||||
html=True,
|
||||
)
|
||||
# Pattern and view name of a RegexURLPattern appear.
|
||||
self.assertContains(response, r"^regex-post/(?P<pk>[0-9]+)/$", status_code=404)
|
||||
self.assertContains(response, "[name='regex-post']", status_code=404)
|
||||
|
@ -130,12 +142,24 @@ class DebugViewTests(SimpleTestCase):
|
|||
@override_settings(ROOT_URLCONF=WithoutEmptyPathUrls)
|
||||
def test_404_empty_path_not_in_urls(self):
|
||||
response = self.client.get('/')
|
||||
self.assertContains(response, 'The empty path didn’t match any of these.', status_code=404)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<p>The empty path didn’t match any of these.</p>',
|
||||
status_code=404,
|
||||
html=True,
|
||||
)
|
||||
|
||||
def test_technical_404(self):
|
||||
response = self.client.get('/technical404/')
|
||||
self.assertContains(response, "Raised by:", status_code=404)
|
||||
self.assertContains(response, "view_tests.views.technical404", status_code=404)
|
||||
self.assertContains(
|
||||
response,
|
||||
'<p>The current path, <code>technical404/</code>, matched the '
|
||||
'last one.</p>',
|
||||
status_code=404,
|
||||
html=True,
|
||||
)
|
||||
|
||||
def test_classbased_technical_404(self):
|
||||
response = self.client.get('/classbased404/')
|
||||
|
|
Loading…
Reference in New Issue