Ticket #13922: resolver-match.diff

File resolver-match.diff, 8.6 KB (added by Nowell Strite, 14 years ago)

Patch against trunk (created from the referenced GitHub branch)

  • django/core/urlresolvers.py

     
    3030# Overridden URLconfs for each thread are stored here.
    3131_urlconfs = {}
    3232
     33class ResolverMatch(object):
     34    def __init__(self, func, args, kwargs, url_name=None, app_name=None, namespaces=[]):
     35        self.func = func
     36        self.args = args
     37        self.kwargs = kwargs
     38        self.app_name = app_name
     39        self.namespaces = [ x for x in namespaces if x ]
     40        if not url_name:
     41            url_name = '.'.join([ func.__module__, func.__name__ ])
     42        self.url_name = url_name
     43
     44    def namespace(self):
     45        return ':'.join(self.namespaces)
     46    namespace = property(namespace)
     47
     48    def view_name(self):
     49        return ':'.join([ x for x in [ self.namespace, self.url_name ]  if x ])
     50    view_name = property(view_name)
     51
     52    def __getitem__(self, index):
     53        return (self.func, self.args, self.kwargs)[index]
     54
     55    def __repr__(self):
     56        return '(%s, %s, %s)' % (self.func, self.args, self.kwargs)
     57
     58    def reverse(self):
     59        return reverse(self.view_name, args=self.args, kwargs=self.kwargs, current_app=self.app_name)
     60
    3361class Resolver404(Http404):
    3462    pass
    3563
     
    120148            # In both cases, pass any extra_kwargs as **kwargs.
    121149            kwargs.update(self.default_args)
    122150
    123             return self.callback, args, kwargs
     151            return ResolverMatch(self.callback, args, kwargs, self.name)
    124152
    125153    def _get_callback(self):
    126154        if self._callback is not None:
     
    224252                    if sub_match:
    225253                        sub_match_dict = dict([(smart_str(k), v) for k, v in match.groupdict().items()])
    226254                        sub_match_dict.update(self.default_kwargs)
    227                         for k, v in sub_match[2].iteritems():
     255                        for k, v in sub_match.kwargs.iteritems():
    228256                            sub_match_dict[smart_str(k)] = v
    229                         return sub_match[0], sub_match[1], sub_match_dict
     257                        return ResolverMatch(sub_match.func, sub_match.args, sub_match_dict, sub_match.url_name, self.app_name or sub_match.app_name, [ self.namespace ] + sub_match.namespaces)
    230258                    tried.append(pattern.regex.pattern)
    231259            raise Resolver404({'tried': tried, 'path': new_path})
    232260        raise Resolver404({'path' : path})
  • tests/regressiontests/urlpatterns_reverse/tests.py

     
    1818
    1919from django.conf import settings
    2020from django.core.exceptions import ImproperlyConfigured
    21 from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404
     21from django.core.urlresolvers import reverse, resolve, NoReverseMatch, Resolver404, ResolverMatch
    2222from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect
    2323from django.shortcuts import redirect
    2424from django.test import TestCase
     
    2626import urlconf_outer
    2727import urlconf_inner
    2828import middleware
     29import views
    2930
     31resolve_test_data = (
     32    # These entries are in the format: (path, url_name, app_name, namespace, view_func, args, kwargs)
     33    ('/normal/42/37/', 'normal-view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}),
     34    ('/included/normal/42/37/', 'inc-normal-view', None, '', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}),
     35    ('/test1/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns1', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
     36    ('/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
     37    ('/ns-included1/normal/42/37/', 'inc-normal-view', None, 'inc-ns1', views.empty_view, tuple(), {'arg1': '42', 'arg2': '37'}),
     38    ('/ns-included1/test3/inner/42/37/', 'urlobject-view', 'testapp', 'inc-ns1:test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
     39    ('/default/inner/42/37/', 'urlobject-view', 'testapp', 'testapp', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
     40    ('/included/test3/inner/42/37/', 'urlobject-view', 'testapp', 'test-ns3', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
     41    ('/other2/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns2', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
     42    ('/other1/inner/42/37/', 'urlobject-view', 'nodefault', 'other-ns1', 'empty_view', tuple(), {'arg1': '42', 'arg2': '37'}),
     43    ('/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'}),
     44    )
     45
    3046test_data = (
    3147    ('places', '/places/3/', [3], {}),
    3248    ('places', '/places/3/', ['3'], {}),
     
    229245        self.assertEquals('/ns-included1/test3/inner/37/42/', reverse('inc-ns1:test-ns3:urlobject-view', args=[37,42]))
    230246        self.assertEquals('/ns-included1/test3/inner/42/37/', reverse('inc-ns1:test-ns3:urlobject-view', kwargs={'arg1':42, 'arg2':37}))
    231247
     248    def test_nested_namespace_pattern(self):
     249        "Namespaces can be nested"
     250        self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view'))
     251        self.assertEquals('/ns-included1/ns-included4/ns-included1/test3/inner/37/42/', reverse('inc-ns1:inc-ns4:inc-ns1:test-ns3:urlobject-view', args=[37,42]))
     252        self.assertEquals('/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}))
     253
    232254    def test_app_lookup_object(self):
    233255        "A default application namespace can be used for lookup"
    234256        self.assertEquals('/default/inner/', reverse('testapp:urlobject-view'))
     
    317339
    318340    def test_no_handler_exception(self):
    319341        self.assertRaises(ImproperlyConfigured, self.client.get, '/test/me/')
     342
     343class ResolverMatchTests(TestCase):
     344    urls = 'regressiontests.urlpatterns_reverse.namespace_urls'
     345
     346    def test_urlpattern_resolve(self):
     347        for path, name, app_name, namespace, func, args, kwargs in resolve_test_data:
     348            # Test legacy support for extracting "function, args, kwargs"
     349            match_func, match_args, match_kwargs = resolve(path)
     350            self.assertEqual(match_func, func)
     351            self.assertEqual(match_args, args)
     352            self.assertEqual(match_kwargs, kwargs)
     353
     354            # Test ResolverMatch capabilities.
     355            match = resolve(path)
     356            self.assertEqual(match.__class__, ResolverMatch)
     357            self.assertEqual(match.url_name, name)
     358            self.assertEqual(match.args, args)
     359            self.assertEqual(match.kwargs, kwargs)
     360            self.assertEqual(match.app_name, app_name)
     361            self.assertEqual(match.namespace, namespace)
     362            self.assertEqual(match.func, func)
  • tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py

     
    99
    1010    (r'^test3/', include(testobj3.urls)),
    1111    (r'^ns-included3/', include('regressiontests.urlpatterns_reverse.included_urls', namespace='inc-ns3')),
     12    (r'^ns-included4/', include('regressiontests.urlpatterns_reverse.namespace_urls', namespace='inc-ns4')),
    1213)
    1314
  • docs/topics/http/urls.txt

     
    858858            return HttpResponseRedirect('/')
    859859        return response
    860860
     861.. versionchanged:: 1.3
     862
     863The :func:`django.core.urlresolvers.resolve` function returns a
     864:class:`django.core.urlresolvers.ResolverMatch` object that allows you to
     865access various meta-data about the resolved URL including (url_name, app_name,
     866namespace, func, args, kwargs).
     867
     868There are two ways to access the results of
     869:func:`django.core.urlresolvers.resolve` in order to maintain backwards
     870compatibility::
     871
     872    # Pre-Django 1.3 support for list expansion.
     873    func, args, kwargs = resolve('/some/path')
     874
     875    # ResolverMatch result.
     876    match = resolve('/some/path')
     877    match.func
     878    match.args
     879    match.kwargs
     880    match.url_name
     881    match.app_name
     882    match.namespace
     883
    861884permalink()
    862885-----------
    863886
Back to Top