Author: jezdez
Date: 2011-08-12 07:15:50 -0700 (Fri, 12 Aug 2011)
New Revision: 16608

Modified:
   django/trunk/django/core/urlresolvers.py
   django/trunk/tests/regressiontests/urlpatterns_reverse/namespace_urls.py
   django/trunk/tests/regressiontests/urlpatterns_reverse/tests.py
Log:
Fixed #11559 -- Fixed the URL resolver to be able to handle captured parameters 
in parent URLconfs when also using namespaces. Thanks, cwb, ungenio and 
hvdklauw.

Modified: django/trunk/django/core/urlresolvers.py
===================================================================
--- django/trunk/django/core/urlresolvers.py    2011-08-12 14:15:41 UTC (rev 
16607)
+++ django/trunk/django/core/urlresolvers.py    2011-08-12 14:15:50 UTC (rev 
16608)
@@ -22,6 +22,7 @@
 
 
 _resolver_cache = {} # Maps URLconf modules to RegexURLResolver instances.
+_ns_resolver_cache = {} # Maps namespaces to RegexURLResolver instances.
 _callable_cache = {} # Maps view and url pattern names to their view functions.
 
 # SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for
@@ -91,20 +92,20 @@
                 lookup_view = getattr(import_module(mod_name), func_name)
                 if not callable(lookup_view):
                     raise ViewDoesNotExist(
-                        "Could not import %s.%s. View is not callable."
-                    % (mod_name, func_name))
+                        "Could not import %s.%s. View is not callable." %
+                        (mod_name, func_name))
         except AttributeError:
             if not can_fail:
                 raise ViewDoesNotExist(
-                    "Could not import %s. View does not exist in module %s."
-                    % (lookup_view, mod_name))
+                    "Could not import %s. View does not exist in module %s." %
+                    (lookup_view, mod_name))
         except ImportError:
             parentmod, submod = get_mod_func(mod_name)
             if (not can_fail and submod != '' and
                     not module_has_submodule(import_module(parentmod), 
submod)):
                 raise ViewDoesNotExist(
-                    "Could not import %s. Parent module %s does not exist."
-                    % (lookup_view, mod_name))
+                    "Could not import %s. Parent module %s does not exist." %
+                    (lookup_view, mod_name))
             if not can_fail:
                 raise
     return lookup_view
@@ -117,6 +118,15 @@
     return RegexURLResolver(r'^/', urlconf)
 get_resolver = memoize(get_resolver, _resolver_cache, 1)
 
+def get_ns_resolver(ns_pattern, resolver):
+    # Build a namespaced resolver for the given parent urlconf pattern.
+    # This makes it possible to have captured parameters in the parent
+    # urlconf pattern.
+    ns_resolver = RegexURLResolver(ns_pattern,
+                                          resolver.url_patterns)
+    return RegexURLResolver(r'^/', [ns_resolver])
+get_ns_resolver = memoize(get_ns_resolver, _ns_resolver_cache, 2)
+
 def get_mod_func(callback):
     # Converts 'django.views.news.stories.story_detail' to
     # ['django.views.news.stories', 'story_detail']
@@ -424,6 +434,7 @@
         path = parts[1:]
 
         resolved_path = []
+        ns_pattern = ''
         while path:
             ns = path.pop()
 
@@ -432,11 +443,13 @@
                 app_list = resolver.app_dict[ns]
                 # Yes! Path part matches an app in the current Resolver
                 if current_app and current_app in app_list:
-                    # If we are reversing for a particular app, use that 
namespace
+                    # If we are reversing for a particular app,
+                    # use that namespace
                     ns = current_app
                 elif ns not in app_list:
-                    # The name isn't shared by one of the instances (i.e., the 
default)
-                    # so just pick the first instance as the default.
+                    # The name isn't shared by one of the instances
+                    # (i.e., the default) so just pick the first instance
+                    # as the default.
                     ns = app_list[0]
             except KeyError:
                 pass
@@ -444,22 +457,29 @@
             try:
                 extra, resolver = resolver.namespace_dict[ns]
                 resolved_path.append(ns)
-                prefix = prefix + extra
+                ns_pattern = ns_pattern + extra
             except KeyError, key:
                 if resolved_path:
-                    raise NoReverseMatch("%s is not a registered namespace 
inside '%s'" % (key, ':'.join(resolved_path)))
+                    raise NoReverseMatch(
+                        "%s is not a registered namespace inside '%s'" %
+                        (key, ':'.join(resolved_path)))
                 else:
-                    raise NoReverseMatch("%s is not a registered namespace" % 
key)
+                    raise NoReverseMatch("%s is not a registered namespace" %
+                                         key)
+        if ns_pattern:
+            resolver = get_ns_resolver(ns_pattern, resolver)
 
-    return iri_to_uri(u'%s%s' % (prefix, resolver.reverse(view,
-            *args, **kwargs)))
+    return iri_to_uri(u'%s%s' %
+                      (prefix, resolver.reverse(view, *args, **kwargs)))
 
 reverse_lazy = lazy(reverse, str)
 
 def clear_url_caches():
     global _resolver_cache
+    global _ns_resolver_cache
     global _callable_cache
     _resolver_cache.clear()
+    _ns_resolver_cache.clear()
     _callable_cache.clear()
 
 def set_script_prefix(prefix):

Modified: 
django/trunk/tests/regressiontests/urlpatterns_reverse/namespace_urls.py
===================================================================
--- django/trunk/tests/regressiontests/urlpatterns_reverse/namespace_urls.py    
2011-08-12 14:15:41 UTC (rev 16607)
+++ django/trunk/tests/regressiontests/urlpatterns_reverse/namespace_urls.py    
2011-08-12 14:15:50 UTC (rev 16608)
@@ -44,4 +44,6 @@
 
     (r'^included/', 
include('regressiontests.urlpatterns_reverse.included_namespace_urls')),
 
+    (r'^ns-outer/(?P<outer>\d+)/', 
include('regressiontests.urlpatterns_reverse.included_namespace_urls', 
namespace='inc-outer')),
+
 )

Modified: django/trunk/tests/regressiontests/urlpatterns_reverse/tests.py
===================================================================
--- django/trunk/tests/regressiontests/urlpatterns_reverse/tests.py     
2011-08-12 14:15:41 UTC (rev 16607)
+++ django/trunk/tests/regressiontests/urlpatterns_reverse/tests.py     
2011-08-12 14:15:50 UTC (rev 16608)
@@ -327,6 +327,13 @@
         self.assertEqual('/ns-included1/normal/37/42/', 
reverse('inc-ns1:inc-normal-view', args=[37,42]))
         self.assertEqual('/ns-included1/normal/42/37/', 
reverse('inc-ns1:inc-normal-view', kwargs={'arg1':42, 'arg2':37}))
 
+    def test_namespace_pattern_with_variable_prefix(self):
+        "When using a include with namespaces when there is a regex variable 
in front of it"
+        self.assertEqual('/ns-outer/42/normal/', 
reverse('inc-outer:inc-normal-view', kwargs={'outer':42}))
+        self.assertEqual('/ns-outer/42/normal/', 
reverse('inc-outer:inc-normal-view', args=[42]))
+        self.assertEqual('/ns-outer/42/normal/37/4/', 
reverse('inc-outer:inc-normal-view', kwargs={'outer':42, 'arg1': 37, 'arg2': 
4}))
+        self.assertEqual('/ns-outer/42/normal/37/4/', 
reverse('inc-outer:inc-normal-view', args=[42, 37, 4]))
+
     def test_multiple_namespace_pattern(self):
         "Namespaces can be embedded"
         self.assertEqual('/ns-included1/test3/inner/', 
reverse('inc-ns1:test-ns3:urlobject-view'))

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to