mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Fixed #31791 -- Made technical 404 debug page display the tried URL patterns for Http404.
This commit is contained in:
		
				
					committed by
					
						 Mariusz Felisiak
						Mariusz Felisiak
					
				
			
			
				
	
			
			
			
						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/') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user