diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py
index 14fc2822815cc8d6675f6b43bbdf091fd60b7393..2bbe0efe49a2ad9f030517eb3024d1c4e85379eb 100644
a
|
b
|
a string) and returns a tuple in this format:
|
8 | 8 | """ |
9 | 9 | from __future__ import unicode_literals |
10 | 10 | |
| 11 | from collections import namedtuple |
11 | 12 | from importlib import import_module |
12 | 13 | import re |
13 | 14 | from threading import local |
… |
… |
_prefixes = local()
|
36 | 37 | # Overridden URLconfs for each thread are stored here. |
37 | 38 | _urlconfs = local() |
38 | 39 | |
| 40 | URLEntry = namedtuple('URLEntry', ['urlbits', 'p_pattern', 'default_args']) |
| 41 | URLBit = namedtuple('URLBit', ['format', 'argument_names']) |
| 42 | |
39 | 43 | |
40 | 44 | class ResolverMatch(object): |
41 | 45 | def __init__(self, func, args, kwargs, url_name=None, app_name=None, namespaces=None): |
… |
… |
class RegexURLResolver(LocaleRegexProvider):
|
269 | 273 | if pattern.app_name: |
270 | 274 | apps.setdefault(pattern.app_name, []).append(pattern.namespace) |
271 | 275 | else: |
272 | | parent = normalize(pattern.regex.pattern) |
| 276 | parent = [URLBit(*p) for p in normalize(pattern.regex.pattern)] |
273 | 277 | for name in pattern.reverse_dict: |
274 | 278 | for matches, pat, defaults in pattern.reverse_dict.getlist(name): |
275 | 279 | new_matches = [] |
276 | 280 | for piece, p_args in parent: |
277 | | new_matches.extend((piece + suffix, p_args + args) for (suffix, args) in matches) |
278 | | lookups.appendlist(name, (new_matches, p_pattern + pat, dict(defaults, **pattern.default_kwargs))) |
| 281 | new_matches.extend([URLBit(piece + suffix, p_args + args) for (suffix, args) in matches]) |
| 282 | lookups.appendlist(name, URLEntry(new_matches, p_pattern + pat, dict(defaults, **pattern.default_kwargs))) |
279 | 283 | for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items(): |
280 | 284 | namespaces[namespace] = (p_pattern + prefix, sub_pattern) |
281 | 285 | for app_name, namespace_list in pattern.app_dict.items(): |
282 | 286 | apps.setdefault(app_name, []).extend(namespace_list) |
283 | 287 | else: |
284 | | bits = normalize(p_pattern) |
285 | | lookups.appendlist(pattern.callback, (bits, p_pattern, pattern.default_args)) |
| 288 | bits = [URLBit(*p) for p in normalize(p_pattern)] |
| 289 | lookups.appendlist(pattern.callback, URLEntry(bits, p_pattern, pattern.default_args)) |
286 | 290 | if pattern.name is not None: |
287 | | lookups.appendlist(pattern.name, (bits, p_pattern, pattern.default_args)) |
| 291 | lookups.appendlist(pattern.name, URLEntry(bits, p_pattern, pattern.default_args)) |
288 | 292 | self._reverse_dict[language_code] = lookups |
289 | 293 | self._namespace_dict[language_code] = namespaces |
290 | 294 | self._app_dict[language_code] = apps |
… |
… |
def is_valid_path(path, urlconf=None):
|
575 | 579 | return True |
576 | 580 | except Resolver404: |
577 | 581 | return False |
| 582 | |
| 583 | def get_urlformat(urlname): |
| 584 | """ |
| 585 | Given a URL name, returns the URL as a string suitable for string.format. |
| 586 | |
| 587 | Example:: |
| 588 | |
| 589 | urlpatterns = patterns('', |
| 590 | url(r'^extra/(?P<extra>\w+)/$', empty_view, name="named-url2"), |
| 591 | ) |
| 592 | |
| 593 | >>> get_urlformat('named-url2') |
| 594 | '/extra/%(extra)s/' |
| 595 | """ |
| 596 | urlconf = get_urlconf() |
| 597 | resolver = get_resolver(urlconf) |
| 598 | urlbit = resolver.reverse_dict[urlname].urlbits[0] |
| 599 | return "%s%s" % (get_script_prefix(), urlbit.format) |
diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py
index 4d77dc946d2f9f0c7fda043fc707235c786eb9f9..9c08356afa485e4c4e15008913ec0a9ee1c37648 100644
a
|
b
|
from django.conf import settings
|
9 | 9 | from django.contrib.auth.models import User |
10 | 10 | from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist |
11 | 11 | from django.core.urlresolvers import (reverse, reverse_lazy, resolve, get_callable, |
12 | | get_resolver, NoReverseMatch, Resolver404, ResolverMatch, RegexURLResolver, |
13 | | RegexURLPattern) |
| 12 | get_resolver, get_urlformat, NoReverseMatch, Resolver404, ResolverMatch, |
| 13 | RegexURLResolver, RegexURLPattern) |
14 | 14 | from django.http import HttpRequest, HttpResponseRedirect, HttpResponsePermanentRedirect |
15 | 15 | from django.shortcuts import redirect |
16 | 16 | from django.test import TestCase |
… |
… |
class ViewLoadingTests(TestCase):
|
657 | 657 | self.assertRaises(AttributeError, get_callable, |
658 | 658 | 'urlpatterns_reverse.views_broken.i_am_broken') |
659 | 659 | |
| 660 | |
| 661 | class URLObjects(TestCase): |
| 662 | urls = 'urlpatterns_reverse.named_urls' |
| 663 | |
| 664 | def test_URL_objects(self): |
| 665 | format = get_urlformat('named-url2') |
| 666 | self.assertEqual(format, '/extra/%(extra)s/') |