2222
2323
2424_resolver_cache = {} # Maps URLconf modules to RegexURLResolver instances.
25+ _ns_resolver_cache = {} # Maps namespaces to RegexURLResolver instances.
2526_callable_cache = {} # Maps view and url pattern names to their view functions.
2627
2728# SCRIPT_NAME prefixes for each thread are stored here. If there's no entry for
@@ -91,20 +92,20 @@ def get_callable(lookup_view, can_fail=False):
9192 lookup_view = getattr (import_module (mod_name ), func_name )
9293 if not callable (lookup_view ):
9394 raise ViewDoesNotExist (
94- "Could not import %s.%s. View is not callable."
95- % (mod_name , func_name ))
95+ "Could not import %s.%s. View is not callable." %
96+ (mod_name , func_name ))
9697 except AttributeError :
9798 if not can_fail :
9899 raise ViewDoesNotExist (
99- "Could not import %s. View does not exist in module %s."
100- % (lookup_view , mod_name ))
100+ "Could not import %s. View does not exist in module %s." %
101+ (lookup_view , mod_name ))
101102 except ImportError :
102103 parentmod , submod = get_mod_func (mod_name )
103104 if (not can_fail and submod != '' and
104105 not module_has_submodule (import_module (parentmod ), submod )):
105106 raise ViewDoesNotExist (
106- "Could not import %s. Parent module %s does not exist."
107- % (lookup_view , mod_name ))
107+ "Could not import %s. Parent module %s does not exist." %
108+ (lookup_view , mod_name ))
108109 if not can_fail :
109110 raise
110111 return lookup_view
@@ -117,6 +118,15 @@ def get_resolver(urlconf):
117118 return RegexURLResolver (r'^/' , urlconf )
118119get_resolver = memoize (get_resolver , _resolver_cache , 1 )
119120
121+ def get_ns_resolver (ns_pattern , resolver ):
122+ # Build a namespaced resolver for the given parent urlconf pattern.
123+ # This makes it possible to have captured parameters in the parent
124+ # urlconf pattern.
125+ ns_resolver = RegexURLResolver (ns_pattern ,
126+ resolver .url_patterns )
127+ return RegexURLResolver (r'^/' , [ns_resolver ])
128+ get_ns_resolver = memoize (get_ns_resolver , _ns_resolver_cache , 2 )
129+
120130def get_mod_func (callback ):
121131 # Converts 'django.views.news.stories.story_detail' to
122132 # ['django.views.news.stories', 'story_detail']
@@ -424,6 +434,7 @@ def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current
424434 path = parts [1 :]
425435
426436 resolved_path = []
437+ ns_pattern = ''
427438 while path :
428439 ns = path .pop ()
429440
@@ -432,34 +443,43 @@ def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current
432443 app_list = resolver .app_dict [ns ]
433444 # Yes! Path part matches an app in the current Resolver
434445 if current_app and current_app in app_list :
435- # If we are reversing for a particular app, use that namespace
446+ # If we are reversing for a particular app,
447+ # use that namespace
436448 ns = current_app
437449 elif ns not in app_list :
438- # The name isn't shared by one of the instances (i.e., the default)
439- # so just pick the first instance as the default.
450+ # The name isn't shared by one of the instances
451+ # (i.e., the default) so just pick the first instance
452+ # as the default.
440453 ns = app_list [0 ]
441454 except KeyError :
442455 pass
443456
444457 try :
445458 extra , resolver = resolver .namespace_dict [ns ]
446459 resolved_path .append (ns )
447- prefix = prefix + extra
460+ ns_pattern = ns_pattern + extra
448461 except KeyError , key :
449462 if resolved_path :
450- raise NoReverseMatch ("%s is not a registered namespace inside '%s'" % (key , ':' .join (resolved_path )))
463+ raise NoReverseMatch (
464+ "%s is not a registered namespace inside '%s'" %
465+ (key , ':' .join (resolved_path )))
451466 else :
452- raise NoReverseMatch ("%s is not a registered namespace" % key )
467+ raise NoReverseMatch ("%s is not a registered namespace" %
468+ key )
469+ if ns_pattern :
470+ resolver = get_ns_resolver (ns_pattern , resolver )
453471
454- return iri_to_uri (u'%s%s' % ( prefix , resolver . reverse ( view ,
455- * args , ** kwargs )))
472+ return iri_to_uri (u'%s%s' %
473+ ( prefix , resolver . reverse ( view , * args , ** kwargs )))
456474
457475reverse_lazy = lazy (reverse , str )
458476
459477def clear_url_caches ():
460478 global _resolver_cache
479+ global _ns_resolver_cache
461480 global _callable_cache
462481 _resolver_cache .clear ()
482+ _ns_resolver_cache .clear ()
463483 _callable_cache .clear ()
464484
465485def set_script_prefix (prefix ):
0 commit comments