Ticket #25409: urltags.patch

File urltags.patch, 9.3 KB (added by Atul Bhouraskar, 9 years ago)

Patch implementing url tagging.

  • 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):  
    9393    return pattern_list
    9494
    9595
    96 def url(regex, view, kwargs=None, name=None, prefix=''):
     96def url(regex, view, kwargs=None, name=None, prefix='', tags=None):
    9797    if isinstance(view, (list, tuple)):
    9898        # For include(...) processing.
    9999        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)
    101102    else:
    102103        if isinstance(view, six.string_types):
    103104            warnings.warn(
    def url(regex, view, kwargs=None, name=None, prefix=''):  
    110111                raise ImproperlyConfigured('Empty URL pattern view name not permitted (for pattern %r)' % regex)
    111112            if prefix:
    112113                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()  
    3737
    3838
    3939class 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):
    4141        self.func = func
    4242        self.args = args
    4343        self.kwargs = kwargs
    class ResolverMatch(object):  
    6464        view_path = url_name or self._func_path
    6565        self.view_name = ':'.join(self.namespaces + [view_path])
    6666
     67        self.tags = set(tags or [])
     68
    6769    def __getitem__(self, index):
    6870        return (self.func, self.args, self.kwargs)[index]
    6971
    7072    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)
    7375
    7476
    7577class Resolver404(Http404):
    class LocaleRegexProvider(object):  
    208210
    209211
    210212class 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):
    212214        LocaleRegexProvider.__init__(self, regex)
    213215        # callback is either a string like 'foo.views.news.stories.story_detail'
    214216        # which represents the path to a module and a view function name, or a
    class RegexURLPattern(LocaleRegexProvider):  
    220222            self._callback_str = callback
    221223        self.default_args = default_args or {}
    222224        self.name = name
     225        self.tags = tags
    223226
    224227    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))
    226229
    227230    def add_prefix(self, prefix):
    228231        """
    class RegexURLPattern(LocaleRegexProvider):  
    246249            # In both cases, pass any extra_kwargs as **kwargs.
    247250            kwargs.update(self.default_args)
    248251
    249             return ResolverMatch(self.callback, args, kwargs, self.name)
     252            return ResolverMatch(self.callback, args, kwargs, self.name, tags=self.tags)
    250253
    251254    @property
    252255    def callback(self):
    class RegexURLPattern(LocaleRegexProvider):  
    258261
    259262
    260263class 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):
    262265        LocaleRegexProvider.__init__(self, regex)
    263266        # urlconf_name is the dotted Python path to the module defining
    264267        # urlpatterns. It may also be an object with an urlpatterns attribute
    class RegexURLResolver(LocaleRegexProvider):  
    268271        self.default_kwargs = default_kwargs or {}
    269272        self.namespace = namespace
    270273        self.app_name = app_name
     274        self.tags = set(tags or [])
    271275        self._reverse_dict = {}
    272276        self._namespace_dict = {}
    273277        self._app_dict = {}
    class RegexURLResolver(LocaleRegexProvider):  
    399403                            sub_match_dict,
    400404                            sub_match.url_name,
    401405                            [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
    403408                        )
    404409                    tried.append([pattern])
    405410            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
    - +  
     1from django.conf.urls import include, url
     2from django.core.urlresolvers import resolve
     3from django.http import HttpResponse
     4from django.test import SimpleTestCase, override_settings
     5
     6import json
     7
     8
     9def 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
     15sub_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
     21sub_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
     28urlpatterns = [
     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')
     37class 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)
Back to Top