Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Debug 404 page now displays names of URL patterns, if they exist.

Thanks to Tobias McNulty for the patch. Fixed #9310.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@13769 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 882cba0f69b1d2c0748b96315b34e3809aa59869 1 parent 4323c22
Malcolm Tredinnick malcolmt authored
6 django/core/urlresolvers.py
View
@@ -252,9 +252,9 @@ def resolve(self, path):
except Resolver404, e:
sub_tried = e.args[0].get('tried')
if sub_tried is not None:
- tried.extend([(pattern.regex.pattern + ' ' + t) for t in sub_tried])
+ tried.extend([[pattern] + t for t in sub_tried])
else:
- tried.append(pattern.regex.pattern)
+ tried.append([pattern])
else:
if sub_match:
sub_match_dict = dict([(smart_str(k), v) for k, v in match.groupdict().items()])
@@ -262,7 +262,7 @@ def resolve(self, path):
for k, v in sub_match.kwargs.iteritems():
sub_match_dict[smart_str(k)] = v
return ResolverMatch(sub_match.func, sub_match.args, sub_match_dict, sub_match.url_name, self.app_name or sub_match.app_name, [self.namespace] + sub_match.namespaces)
- tried.append(pattern.regex.pattern)
+ tried.append([pattern])
raise Resolver404({'tried': tried, 'path': new_path})
raise Resolver404({'path' : path})
7 django/views/debug.py
View
@@ -778,7 +778,12 @@ def empty_urlconf(request):
</p>
<ol>
{% for pattern in urlpatterns %}
- <li>{{ pattern }}</li>
+ <li>
+ {% for pat in pattern %}
+ {{ pat.regex.pattern }}
+ {% if forloop.last and pat.name %}[name='{{ pat.name }}']{% endif %}
+ {% endfor %}
+ </li>
{% endfor %}
</ol>
<p>The current URL, <code>{{ request_path|escape }}</code>, didn't match any of these.</p>
10 tests/regressiontests/urlpatterns_reverse/included_named_urls.py
View
@@ -0,0 +1,10 @@
+from django.conf.urls.defaults import *
+from views import empty_view
+
+urlpatterns = patterns('',
+ url(r'^$', empty_view, name="named-url3"),
+ url(r'^extra/(?P<extra>\w+)/$', empty_view, name="named-url4"),
+ url(r'^(?P<one>\d+)|(?P<two>\d+)/$', empty_view),
+ (r'^included/', include('regressiontests.urlpatterns_reverse.included_named_urls2')),
+)
+
9 tests/regressiontests/urlpatterns_reverse/included_named_urls2.py
View
@@ -0,0 +1,9 @@
+from django.conf.urls.defaults import *
+from views import empty_view
+
+urlpatterns = patterns('',
+ url(r'^$', empty_view, name="named-url5"),
+ url(r'^extra/(?P<extra>\w+)/$', empty_view, name="named-url6"),
+ url(r'^(?P<one>\d+)|(?P<two>\d+)/$', empty_view),
+)
+
9 tests/regressiontests/urlpatterns_reverse/named_urls.py
View
@@ -0,0 +1,9 @@
+from django.conf.urls.defaults import *
+from views import empty_view
+
+urlpatterns = patterns('',
+ url(r'^$', empty_view, name="named-url1"),
+ url(r'^extra/(?P<extra>\w+)/$', empty_view, name="named-url2"),
+ url(r'^(?P<one>\d+)|(?P<two>\d+)/$', empty_view),
+ (r'^included/', include('regressiontests.urlpatterns_reverse.included_named_urls')),
+)
40 tests/regressiontests/urlpatterns_reverse/tests.py
View
@@ -18,7 +18,9 @@
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
-from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404, ResolverMatch
+from django.core.urlresolvers import reverse, resolve, NoReverseMatch,\
+ Resolver404, ResolverMatch,\
+ RegexURLResolver, RegexURLPattern
from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
from django.shortcuts import redirect
from django.test import TestCase
@@ -172,6 +174,42 @@ def test_non_regex(self):
self.assertRaises(Resolver404, resolve, 'a')
self.assertRaises(Resolver404, resolve, '\\')
self.assertRaises(Resolver404, resolve, '.')
+
+ def test_404_tried_urls_have_names(self):
+ """
+ Verifies that the list of URLs that come back from a Resolver404
+ exception contains a list in the right format for printing out in
+ the DEBUG 404 page with both the patterns and URL names, if available.
+ """
+ urls = 'regressiontests.urlpatterns_reverse.named_urls'
+ # this list matches the expected URL types and names returned when
+ # you try to resolve a non-existent URL in the first level of included
+ # URLs in named_urls.py (e.g., '/included/non-existent-url')
+ url_types_names = [
+ [{'type': RegexURLPattern, 'name': 'named-url1'}],
+ [{'type': RegexURLPattern, 'name': 'named-url2'}],
+ [{'type': RegexURLPattern, 'name': None}],
+ [{'type': RegexURLResolver}, {'type': RegexURLPattern, 'name': 'named-url3'}],
+ [{'type': RegexURLResolver}, {'type': RegexURLPattern, 'name': 'named-url4'}],
+ [{'type': RegexURLResolver}, {'type': RegexURLPattern, 'name': None}],
+ [{'type': RegexURLResolver}, {'type': RegexURLResolver}],
+ ]
+ try:
+ resolve('/included/non-existent-url', urlconf=urls)
+ self.fail('resolve did not raise a 404')
+ except Resolver404, e:
+ # make sure we at least matched the root ('/') url resolver:
+ self.assertTrue('tried' in e.args[0])
+ tried = e.args[0]['tried']
+ self.assertEqual(len(e.args[0]['tried']), len(url_types_names), 'Wrong number of tried URLs returned. Expected %s, got %s.' % (len(url_types_names), len(e.args[0]['tried'])))
+ for tried, expected in zip(e.args[0]['tried'], url_types_names):
+ for t, e in zip(tried, expected):
+ self.assertTrue(isinstance(t, e['type']), '%s is not an instance of %s' % (t, e['type']))
+ if 'name' in e:
+ if not e['name']:
+ self.assertTrue(t.name is None, 'Expected no URL name but found %s.' % t.name)
+ else:
+ self.assertEqual(t.name, e['name'], 'Wrong URL name. Expected "%s", got "%s".' % (e['name'], t.name))
class ReverseShortcutTests(TestCase):
urls = 'regressiontests.urlpatterns_reverse.urls'
Please sign in to comment.
Something went wrong with that request. Please try again.