diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
index b634b56..84cb111 100644
|
|
|
def get_resolver(urlconf):
|
| 118 | 118 | return RegexURLResolver(r'^/', urlconf) |
| 119 | 119 | get_resolver = memoize(get_resolver, _resolver_cache, 1) |
| 120 | 120 | |
| 121 | | def get_ns_resolver(ns_pattern, resolver): |
| | 121 | def get_ns_resolver(resolvers): |
| 122 | 122 | # Build a namespaced resolver for the given parent urlconf pattern. |
| 123 | 123 | # This makes it possible to have captured parameters in the parent |
| 124 | 124 | # urlconf pattern. |
| 125 | | ns_resolver = RegexURLResolver(ns_pattern, |
| 126 | | resolver.url_patterns) |
| 127 | | return RegexURLResolver(r'^/', [ns_resolver]) |
| | 125 | ns_resolvers = [] |
| | 126 | for ns_pattern, resolver in resolvers: |
| | 127 | ns_resolvers.append(RegexURLResolver(ns_pattern, |
| | 128 | resolver.url_patterns)) |
| | 129 | return RegexURLResolver(r'^/', ns_resolvers) |
| 128 | 130 | get_ns_resolver = memoize(get_ns_resolver, _ns_resolver_cache, 2) |
| 129 | 131 | |
| 130 | 132 | def get_mod_func(callback): |
| … |
… |
class RegexURLResolver(LocaleRegexProvider):
|
| 245 | 247 | p_pattern = p_pattern[1:] |
| 246 | 248 | if isinstance(pattern, RegexURLResolver): |
| 247 | 249 | if pattern.namespace: |
| 248 | | namespaces[pattern.namespace] = (p_pattern, pattern) |
| | 250 | namespaces.setdefault(pattern.namespace, []).append((p_pattern, pattern)) |
| 249 | 251 | if pattern.app_name: |
| 250 | 252 | apps.setdefault(pattern.app_name, []).append(pattern.namespace) |
| 251 | 253 | else: |
| … |
… |
class RegexURLResolver(LocaleRegexProvider):
|
| 256 | 258 | for piece, p_args in parent: |
| 257 | 259 | new_matches.extend([(piece + suffix, p_args + args) for (suffix, args) in matches]) |
| 258 | 260 | lookups.appendlist(name, (new_matches, p_pattern + pat, dict(defaults, **pattern.default_kwargs))) |
| 259 | | for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items(): |
| 260 | | namespaces[namespace] = (p_pattern + prefix, sub_pattern) |
| | 261 | for namespace, resolvers in pattern.namespace_dict.items(): |
| | 262 | for prefix, sub_pattern in resolvers: |
| | 263 | namespaces.setdefault(namespace, []).append((p_pattern + prefix, sub_pattern)) |
| 261 | 264 | for app_name, namespace_list in pattern.app_dict.items(): |
| 262 | 265 | apps.setdefault(app_name, []).extend(namespace_list) |
| 263 | 266 | else: |
| … |
… |
def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current
|
| 437 | 440 | view = parts[0] |
| 438 | 441 | path = parts[1:] |
| 439 | 442 | |
| | 443 | resolvers = [] |
| 440 | 444 | resolved_path = [] |
| 441 | | ns_pattern = '' |
| | 445 | ns_patterns = [''] |
| 442 | 446 | while path: |
| 443 | 447 | ns = path.pop() |
| 444 | 448 | |
| … |
… |
def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current
|
| 459 | 463 | pass |
| 460 | 464 | |
| 461 | 465 | try: |
| 462 | | extra, resolver = resolver.namespace_dict[ns] |
| | 466 | _resolvers = resolver.namespace_dict[ns] |
| 463 | 467 | resolved_path.append(ns) |
| 464 | | ns_pattern = ns_pattern + extra |
| | 468 | _ns_patterns = [] |
| | 469 | for extra, resolver in _resolvers: |
| | 470 | for ns_pattern in ns_patterns: |
| | 471 | ns_pattern += extra |
| | 472 | _ns_patterns.append(ns_pattern) |
| | 473 | resolvers.append((ns_pattern, resolver)) |
| 465 | 474 | except KeyError, key: |
| 466 | 475 | if resolved_path: |
| 467 | 476 | raise NoReverseMatch( |
| … |
… |
def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current
|
| 470 | 479 | else: |
| 471 | 480 | raise NoReverseMatch("%s is not a registered namespace" % |
| 472 | 481 | key) |
| 473 | | if ns_pattern: |
| 474 | | resolver = get_ns_resolver(ns_pattern, resolver) |
| | 482 | |
| | 483 | if resolvers and resolvers[0]: |
| | 484 | resolver = get_ns_resolver(tuple(resolvers)) |
| 475 | 485 | |
| 476 | 486 | return iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)) |
| 477 | 487 | |
diff --git a/tests/regressiontests/urlpatterns_reverse/namespace_urls.py b/tests/regressiontests/urlpatterns_reverse/namespace_urls.py
index fa892a4..2208d38 100644
|
|
|
from .views import view_class_instance
|
| 6 | 6 | |
| 7 | 7 | |
| 8 | 8 | class URLObject(object): |
| 9 | | def __init__(self, app_name, namespace): |
| | 9 | def __init__(self, app_name, namespace, _patterns=None): |
| | 10 | if _patterns is None: |
| | 11 | _patterns = patterns('', |
| | 12 | url(r'^inner/$', 'empty_view', name='urlobject-view'), |
| | 13 | url(r'^inner/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='urlobject-view'), |
| | 14 | url(r'^inner/\+\\\$\*/$', 'empty_view', name='urlobject-special-view'), |
| | 15 | ) |
| | 16 | self.patterns = _patterns |
| 10 | 17 | self.app_name = app_name |
| 11 | 18 | self.namespace = namespace |
| 12 | 19 | |
| 13 | 20 | def urls(self): |
| 14 | | return patterns('', |
| 15 | | url(r'^inner/$', 'empty_view', name='urlobject-view'), |
| 16 | | url(r'^inner/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='urlobject-view'), |
| 17 | | url(r'^inner/\+\\\$\*/$', 'empty_view', name='urlobject-special-view'), |
| 18 | | ), self.app_name, self.namespace |
| | 21 | return self.patterns, self.app_name, self.namespace |
| 19 | 22 | urls = property(urls) |
| 20 | 23 | |
| 21 | 24 | testobj1 = URLObject('testapp', 'test-ns1') |
| … |
… |
default_testobj = URLObject('testapp', 'testapp')
|
| 25 | 28 | otherobj1 = URLObject('nodefault', 'other-ns1') |
| 26 | 29 | otherobj2 = URLObject('nodefault', 'other-ns2') |
| 27 | 30 | |
| | 31 | first_patterns = patterns('', |
| | 32 | url(r'^$', 'empty_view', name='first'), |
| | 33 | ) |
| | 34 | first = URLObject('first', 'inc-some', first_patterns) |
| | 35 | second_patterns = patterns('', |
| | 36 | url(r'^$', 'empty_view', name='second'), |
| | 37 | ) |
| | 38 | second = URLObject('second', 'inc-some', second_patterns) |
| | 39 | |
| 28 | 40 | urlpatterns = patterns('regressiontests.urlpatterns_reverse.views', |
| 29 | 41 | url(r'^normal/$', 'empty_view', name='normal-view'), |
| 30 | 42 | url(r'^normal/(?P<arg1>\d+)/(?P<arg2>\d+)/$', 'empty_view', name='normal-view'), |
| … |
… |
urlpatterns = patterns('regressiontests.urlpatterns_reverse.views',
|
| 55 | 67 | (r'^ns-outer/(?P<outer>\d+)/', include('regressiontests.urlpatterns_reverse.included_namespace_urls', namespace='inc-outer')), |
| 56 | 68 | |
| 57 | 69 | (r'^\+\\\$\*/', include('regressiontests.urlpatterns_reverse.namespace_urls', namespace='special')), |
| | 70 | |
| | 71 | url(r'^first/', include(first.urls)), |
| | 72 | url(r'^second/', include(second.urls)), |
| 58 | 73 | ) |
diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py
index a1c9244..aef7214 100644
|
|
|
class NamespaceTests(TestCase):
|
| 392 | 392 | self.assertEqual('/inc70/', reverse('inc-ns5:inner-nothing', args=['70'])) |
| 393 | 393 | self.assertEqual('/inc78/extra/foobar/', reverse('inc-ns5:inner-extra', args=['78','foobar'])) |
| 394 | 394 | |
| | 395 | def test_namespaces_in_different_includes(self): |
| | 396 | "Namespaces declared in different places: see #17551" |
| | 397 | self.assertEqual('/first/', reverse('inc-some:first')) |
| | 398 | self.assertEqual('/second/', reverse('inc-some:second')) |
| | 399 | |
| 395 | 400 | class RequestURLconfTests(TestCase): |
| 396 | 401 | def setUp(self): |
| 397 | 402 | self.root_urlconf = settings.ROOT_URLCONF |