Ticket #25409: urltags.patch
File urltags.patch, 9.3 KB (added by , 9 years ago) |
---|
-
django/conf/urls/__init__.py
diff --git a/django/conf/urls/__init__.py b/django/conf/urls/__init__.py index ab67fd3..916c3bb 100644
a b def patterns(prefix, *args): 93 93 return pattern_list 94 94 95 95 96 def url(regex, view, kwargs=None, name=None, prefix='' ):96 def url(regex, view, kwargs=None, name=None, prefix='', tags=None): 97 97 if isinstance(view, (list, tuple)): 98 98 # For include(...) processing. 99 99 urlconf_module, app_name, namespace = view 100 return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace) 100 return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace, 101 tags=tags) 101 102 else: 102 103 if isinstance(view, six.string_types): 103 104 warnings.warn( … … def url(regex, view, kwargs=None, name=None, prefix=''): 110 111 raise ImproperlyConfigured('Empty URL pattern view name not permitted (for pattern %r)' % regex) 111 112 if prefix: 112 113 view = prefix + '.' + view 113 return RegexURLPattern(regex, view, kwargs, name )114 return RegexURLPattern(regex, view, kwargs, name, tags=tags) -
django/core/urlresolvers.py
diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index e462c34..c0b95d1 100644
a b _urlconfs = local() 37 37 38 38 39 39 class ResolverMatch(object): 40 def __init__(self, func, args, kwargs, url_name=None, app_names=None, namespaces=None ):40 def __init__(self, func, args, kwargs, url_name=None, app_names=None, namespaces=None, tags=None): 41 41 self.func = func 42 42 self.args = args 43 43 self.kwargs = kwargs … … class ResolverMatch(object): 64 64 view_path = url_name or self._func_path 65 65 self.view_name = ':'.join(self.namespaces + [view_path]) 66 66 67 self.tags = set(tags or []) 68 67 69 def __getitem__(self, index): 68 70 return (self.func, self.args, self.kwargs)[index] 69 71 70 72 def __repr__(self): 71 return "ResolverMatch(func=%s, args=%s, kwargs=%s, url_name=%s, app_names=%s, namespaces=%s )" % (72 self._func_path, self.args, self.kwargs, self.url_name, self.app_names, self.namespaces )73 return "ResolverMatch(func=%s, args=%s, kwargs=%s, url_name=%s, app_names=%s, namespaces=%s, tags=%s)" % ( 74 self._func_path, self.args, self.kwargs, self.url_name, self.app_names, self.namespaces, self.tags) 73 75 74 76 75 77 class Resolver404(Http404): … … class LocaleRegexProvider(object): 208 210 209 211 210 212 class RegexURLPattern(LocaleRegexProvider): 211 def __init__(self, regex, callback, default_args=None, name=None ):213 def __init__(self, regex, callback, default_args=None, name=None, tags=None): 212 214 LocaleRegexProvider.__init__(self, regex) 213 215 # callback is either a string like 'foo.views.news.stories.story_detail' 214 216 # which represents the path to a module and a view function name, or a … … class RegexURLPattern(LocaleRegexProvider): 220 222 self._callback_str = callback 221 223 self.default_args = default_args or {} 222 224 self.name = name 225 self.tags = tags 223 226 224 227 def __repr__(self): 225 return force_str('<%s %s %s >' % (self.__class__.__name__, self.name, self.regex.pattern))228 return force_str('<%s %s %s %s>' % (self.__class__.__name__, self.name, self.regex.pattern, self.tags)) 226 229 227 230 def add_prefix(self, prefix): 228 231 """ … … class RegexURLPattern(LocaleRegexProvider): 246 249 # In both cases, pass any extra_kwargs as **kwargs. 247 250 kwargs.update(self.default_args) 248 251 249 return ResolverMatch(self.callback, args, kwargs, self.name )252 return ResolverMatch(self.callback, args, kwargs, self.name, tags=self.tags) 250 253 251 254 @property 252 255 def callback(self): … … class RegexURLPattern(LocaleRegexProvider): 258 261 259 262 260 263 class RegexURLResolver(LocaleRegexProvider): 261 def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None ):264 def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None, tags=None): 262 265 LocaleRegexProvider.__init__(self, regex) 263 266 # urlconf_name is the dotted Python path to the module defining 264 267 # urlpatterns. It may also be an object with an urlpatterns attribute … … class RegexURLResolver(LocaleRegexProvider): 268 271 self.default_kwargs = default_kwargs or {} 269 272 self.namespace = namespace 270 273 self.app_name = app_name 274 self.tags = set(tags or []) 271 275 self._reverse_dict = {} 272 276 self._namespace_dict = {} 273 277 self._app_dict = {} … … class RegexURLResolver(LocaleRegexProvider): 399 403 sub_match_dict, 400 404 sub_match.url_name, 401 405 [self.app_name] + sub_match.app_names, 402 [self.namespace] + sub_match.namespaces 406 [self.namespace] + sub_match.namespaces, 407 tags=self.tags.union(sub_match.tags) # Apply tags to included patterns 403 408 ) 404 409 tried.append([pattern]) 405 410 raise Resolver404({'tried': tried, 'path': new_path}) -
new file tests/urltags/tests.py
diff --git a/tests/urltags/__init__.py b/tests/urltags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/urltags/tests.py b/tests/urltags/tests.py new file mode 100644 index 0000000..7b67c1f
- + 1 from django.conf.urls import include, url 2 from django.core.urlresolvers import resolve 3 from django.http import HttpResponse 4 from django.test import SimpleTestCase, override_settings 5 6 import json 7 8 9 def test_view(request): 10 """ 11 Returns the tags attached to the request in the response, so they can be veried in tests 12 """ 13 return HttpResponse(json.dumps(list(request.resolver_match.tags))) 14 15 sub_sub_patterns = [ 16 url(r'^a/$', test_view, name='s-s-p1'), 17 url(r'^b/$', test_view, name='s-s-p2', tags=['s-s-t1', 's-s-t2']), 18 url(r'^c/$', test_view, name='s-s-p3', tags=['s-s-t1', 't4']), 19 ] 20 21 sub_patterns = [ 22 url(r'^a/$', test_view, name='s-p1'), 23 url(r'^b/$', test_view, name='s-p2', tags=['s-t1']), 24 url(r'^c/', include(sub_sub_patterns), tags=['s-t2']), 25 26 ] 27 28 urlpatterns = [ 29 url(r'^$', test_view, name='p1'), 30 url(r'^a/$', test_view, name='p2', tags=['t1']), 31 url(r'^b/$', test_view, name='p2', tags=['t2', 't3']), 32 url(r'^c/', include(sub_patterns), tags=['t4']), 33 ] 34 35 36 @override_settings(ROOT_URLCONF='urltags.tests') 37 class URLTagsTests(SimpleTestCase): 38 def test_no_tags(self): 39 """ 40 Verifies url pattern with no tags specified 41 """ 42 match = resolve('/') 43 self.assertEqual(len(match.tags), 0) 44 45 def test_single_tag(self): 46 """ 47 Verifies url pattern with a single tag 48 """ 49 match = resolve('/a/') 50 self.assertEqual(len(match.tags), 1) 51 self.assertTrue('t1' in match.tags) 52 53 def test_multiple_tags(self): 54 """ 55 Verifies url pattern with a multiple tags 56 """ 57 match = resolve('/b/') 58 self.assertEqual(len(match.tags), 2) 59 self.assertTrue('t2' in match.tags) 60 self.assertTrue('t3' in match.tags) 61 62 def test_include1(self): 63 """ 64 Verifies that a single tag is correctly propagated to included pattern 65 """ 66 match = resolve('/c/a/') 67 self.assertEqual(len(match.tags), 1) 68 self.assertTrue('t4' in match.tags) 69 70 def test_include2(self): 71 """ 72 Verifies that tags are correctly combined with included patterns 73 """ 74 match = resolve('/c/b/') 75 self.assertEqual(len(match.tags), 2) 76 self.assertTrue('t4' in match.tags) 77 self.assertTrue('s-t1' in match.tags) 78 79 def test_include3(self): 80 """ 81 Verifies that tags are correctly combined with multi-level included patterns 82 """ 83 match = resolve('/c/c/a/') 84 self.assertEqual(len(match.tags), 2) 85 self.assertTrue('t4' in match.tags) 86 self.assertTrue('s-t2' in match.tags) 87 88 def test_include4(self): 89 """ 90 Verifies that multiple tags are correctly combined with multi-level included patterns 91 """ 92 match = resolve('/c/c/b/') 93 self.assertEqual(len(match.tags), 4) 94 self.assertTrue('t4' in match.tags) 95 self.assertTrue('s-t2' in match.tags) 96 self.assertTrue('s-s-t1' in match.tags) 97 self.assertTrue('s-s-t2' in match.tags) 98 99 def test_include5(self): 100 """ 101 Verifies that tags repeated in included patterns are correctly handled 102 """ 103 match = resolve('/c/c/c/') 104 self.assertEqual(len(match.tags), 3) 105 self.assertTrue('t4' in match.tags) 106 self.assertTrue('s-t2' in match.tags) 107 self.assertTrue('s-s-t1' in match.tags) 108 109 def test_request(self): 110 """ 111 Verifies that tags can be accessed from within the request object 112 """ 113 response = self.client.get('/c/c/c/') 114 tags = set(json.loads(response.content)) 115 self.assertEqual(len(tags), 3) 116 self.assertTrue('t4' in tags) 117 self.assertTrue('s-t2' in tags) 118 self.assertTrue('s-s-t1' in tags)