Code

Ticket #15900: 15900_normalize_namespace_prefixes_on_reverse.2.diff

File 15900_normalize_namespace_prefixes_on_reverse.2.diff, 5.6 KB (added by dmclain, 3 years ago)

Adding a resolve test for variable capturing namespaced paths

Line 
1diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
2index 52d9090..0db5f11 100644
3--- a/django/core/urlresolvers.py
4+++ b/django/core/urlresolvers.py
5@@ -302,7 +302,8 @@ class RegexURLResolver(object):
6     def resolve500(self):
7         return self._resolve_special('500')
8 
9-    def reverse(self, lookup_view, *args, **kwargs):
10+    def reverse(self, lookup_view, *args, **kwargs):   
11+        prefix = kwargs.pop('__DJANGO_REVERSE_PREFIX','')
12         if args and kwargs:
13             raise ValueError("Don't mix *args and **kwargs in call to reverse()!")
14         try:
15@@ -310,19 +311,20 @@ class RegexURLResolver(object):
16         except (ImportError, AttributeError), e:
17             raise NoReverseMatch("Error importing '%s': %s." % (lookup_view, e))
18         possibilities = self.reverse_dict.getlist(lookup_view)
19+        prefix_norm, prefix_args = normalize(prefix)[0]
20         for possibility, pattern in possibilities:
21             for result, params in possibility:
22                 if args:
23-                    if len(args) != len(params):
24+                    if len(args) != len(params) + len(prefix_args):
25                         continue
26                     unicode_args = [force_unicode(val) for val in args]
27-                    candidate =  result % dict(zip(params, unicode_args))
28+                    candidate =  (prefix_norm + result) % dict(zip(params, unicode_args))
29                 else:
30-                    if set(kwargs.keys()) != set(params):
31+                    if set(kwargs.keys()) != (set(params)|set(prefix_args)):
32                         continue
33                     unicode_kwargs = dict([(k, force_unicode(v)) for (k, v) in kwargs.items()])
34-                    candidate = result % unicode_kwargs
35-                if re.search(u'^%s' % pattern, candidate, re.UNICODE):
36+                    candidate = (prefix_norm + result) % unicode_kwargs
37+                if re.search(u'^%s%s' % (prefix, pattern), candidate, re.UNICODE):
38                     return candidate
39         # lookup_view can be URL label, or dotted path, or callable, Any of
40         # these can be passed in at the top, but callables are not friendly in
41@@ -386,9 +388,8 @@ def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current
42                     raise NoReverseMatch("%s is not a registered namespace inside '%s'" % (key, ':'.join(resolved_path)))
43                 else:
44                     raise NoReverseMatch("%s is not a registered namespace" % key)
45-
46-    return iri_to_uri(u'%s%s' % (prefix, resolver.reverse(view,
47-            *args, **kwargs)))
48+    kwargs['__DJANGO_REVERSE_PREFIX'] = prefix
49+    return iri_to_uri(resolver.reverse(view, *args, **kwargs))
50 
51 reverse_lazy = lazy(reverse, str)
52 
53diff --git a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py
54index 3d34049..0544e75 100644
55--- a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py
56+++ b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py
57@@ -37,11 +37,12 @@ urlpatterns = patterns('regressiontests.urlpatterns_reverse.views',
58     (r'^default/', include(default_testobj.urls)),
59 
60     (r'^other1/', include(otherobj1.urls)),
61-    (r'^other2/', include(otherobj2.urls)),
62+    (r'^other[246]/', include(otherobj2.urls)),
63 
64-    (r'^ns-included1/', include('regressiontests.urlpatterns_reverse.included_namespace_urls', namespace='inc-ns1')),
65+    (r'^ns-included[135]/', include('regressiontests.urlpatterns_reverse.included_namespace_urls', namespace='inc-ns1')),
66     (r'^ns-included2/', include('regressiontests.urlpatterns_reverse.included_namespace_urls', namespace='inc-ns2')),
67 
68     (r'^included/', include('regressiontests.urlpatterns_reverse.included_namespace_urls')),
69+    (r'^inc(?P<outer>\d+)/', include('regressiontests.urlpatterns_reverse.included_urls', namespace='inc-ns5')),
70 
71 )
72diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py
73index 4f391d9..71e1913 100644
74--- a/tests/regressiontests/urlpatterns_reverse/tests.py
75+++ b/tests/regressiontests/urlpatterns_reverse/tests.py
76@@ -49,6 +49,11 @@ resolve_test_data = (
77     # Nested namespaces
78     ('/ns-included1/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
79     ('/ns-included1/ns-included4/ns-included2/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:inc-ns4:inc-ns2:test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
80+
81+    # Namespaces capturing variables
82+    ('/inc70/', 'inner-nothing', None, 'inc-ns5', views.empty_view, tuple(), {'outer': '70'}),
83+    ('/inc78/extra/foobar/', 'inner-extra', None, 'inc-ns5', views.empty_view, tuple(), {'outer':'78', 'extra':'foobar'}),
84+   
85 )
86 
87 test_data = (
88@@ -335,6 +340,10 @@ class NamespaceTests(TestCase):
89         self.assertEqual('/ns-included1/ns-included4/ns-included1/test3/inner/37/42/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view', args=[37,42]))
90         self.assertEqual('/ns-included1/ns-included4/ns-included1/test3/inner/42/37/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37}))
91 
92+    def test_nested_namespace_with_variable(self):
93+        self.assertEqual('/inc54/', reverse('inc-ns5:inner-nothing', kwargs={'outer':'54'}))
94+        self.assertEqual('/inc78/extra/foobar/', reverse('inc-ns5:inner-extra', kwargs={'outer':'78', 'extra':'foobar'}))
95+
96     def test_app_lookup_object(self):
97         "A default application namespace can be used for lookup"
98         self.assertEqual('/default/inner/', reverse('testapp:urlobject-view'))