Ticket #3402: django_stem.patch

File django_stem.patch, 11.5 KB (added by medhat, 17 years ago)

patch with documentation and tests

  • django/conf/urls/defaults.py

     
    1 from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
     1from django.core.urlresolvers import RegexURLPattern, RegexURLResolver, Stem
    22
    3 __all__ = ['handler404', 'handler500', 'include', 'patterns']
     3__all__ = ['handler404', 'handler500', 'include', 'patterns', 'stem']
    44
    55handler404 = 'django.views.defaults.page_not_found'
    66handler500 = 'django.views.defaults.server_error'
    77
     8stem = Stem()
     9
    810include = lambda urlconf_module: [urlconf_module]
    911
    1012def patterns(prefix, *tuples):
  • django/core/handlers/base.py

     
    6565
    6666        resolver = urlresolvers.RegexURLResolver(r'^/', urlconf)
    6767        try:
    68             callback, callback_args, callback_kwargs = resolver.resolve(request.path)
     68            callback, callback_args, callback_kwargs, stem = resolver.resolve(request.path)
    6969
    7070            # Apply view middleware
    7171            for middleware_method in self._view_middleware:
  • django/core/urlresolvers.py

     
    1111from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist
    1212import re
    1313
     14class Stem(object):
     15    pass
     16
    1417class Resolver404(Http404):
    1518    pass
    1619
    1720class NoReverseMatch(Exception):
    1821    # Don't make this raise an error when used in a template.
    19     silent_variable_failure = True 
     22    silent_variable_failure = True
    2023
    2124def get_mod_func(callback):
    2225    # Converts 'django.views.news.stories.story_detail' to
     
    101104            self._callback_str = callback
    102105        self.default_args = default_args or {}
    103106
    104     def resolve(self, path):
     107        self._stem = None
     108        for k,v in self.default_args.iteritems():
     109            if type(v) == Stem:
     110                self._stem = k
     111                break
     112
     113    def resolve(self, path, stem=''):
    105114        match = self.regex.search(path)
    106115        if match:
    107116            # If there are any named groups, use those as kwargs, ignoring
     
    113122            if not kwargs:
    114123                args = match.groups()
    115124            # In both cases, pass any extra_kwargs as **kwargs.
     125            if self._stem:
     126                self.default_args[self._stem] = stem
    116127            kwargs.update(self.default_args)
    117128
    118             return self.callback, args, kwargs
     129            return self.callback, args, kwargs, stem
    119130
    120131    def _get_callback(self):
    121132        if self._callback is not None:
     
    152163        self.callback = None
    153164        self.default_kwargs = default_kwargs or {}
    154165
    155     def resolve(self, path):
     166        self._stem = None
     167        for k,v in self.default_kwargs.iteritems():
     168            if type(v) == Stem:
     169                self._stem = k
     170                break
     171
     172    def resolve(self, path, stem=''):
    156173        tried = []
    157174        match = self.regex.search(path)
    158175        if match:
     176            stem += path[:match.end()]
    159177            new_path = path[match.end():]
    160178            for pattern in self.urlconf_module.urlpatterns:
    161179                try:
    162                     sub_match = pattern.resolve(new_path)
     180                    sub_match = pattern.resolve(new_path, stem)
    163181                except Resolver404, e:
    164182                    tried.extend([(pattern.regex.pattern + '   ' + t) for t in e.args[0]['tried']])
    165183                else:
    166184                    if sub_match:
     185                        if self._stem:
     186                            self.default_kwargs[self._stem] = sub_match[3]
    167187                        sub_match_dict = dict(self.default_kwargs, **sub_match[2])
    168                         return sub_match[0], sub_match[1], dict(match.groupdict(), **sub_match_dict)
     188                        return sub_match[0], sub_match[1], dict(match.groupdict(), **sub_match_dict), sub_match[3]
    169189                    tried.append(pattern.regex.pattern)
    170190            raise Resolver404, {'tried': tried, 'path': new_path}
    171191
     
    229249        from django.conf import settings
    230250        urlconf = settings.ROOT_URLCONF
    231251    resolver = RegexURLResolver(r'^/', urlconf)
    232     return resolver.resolve(path)
     252    return resolver.resolve(path)[:3]
    233253
    234254def reverse(viewname, urlconf=None, args=None, kwargs=None):
    235255    args = args or []
  • docs/url_dispatch.txt

     
    215215A function that takes a full Python import path to another URLconf that should
    216216be "included" in this place. See _`Including other URLconfs` below.
    217217
     218stem
     219----
     220
     221** New in Django development version **
     222
     223An object that you can use in the optional dictionary to pass the part of the
     224url that matched upto the point before matching against patterns in the current
     225URLconf. See _`Including other URLconfs` below.
     226
    218227Notes on capturing text in URLs
    219228===============================
    220229
     
    364373In the above example, the captured ``"username"`` variable is passed to the
    365374included URLconf, as expected.
    366375
     376Passing the stem of the matched URL
     377-----------------------------------
     378
     379** New in Django development version **
     380
     381In the following example::
     382
     383    # In settings/urls/main.py
     384    urlpatterns = patterns('',
     385        (r'^blog/', include('foo.urls.blog')),
     386    )
     387
     388    # In foo/urls/blog.py
     389    urlpatterns = patterns('foo.views',
     390        (r'^$', 'blog.index', {'stem':stem}),
     391        (r'^archive/$', 'blog.archive', {'stem':stem}),
     392    )
     393   
     394In the above example, both blog.index and blog.archive will be passed a kwarg
     395called 'stem', and having the value '/blog/' (coming from settings/urls/main.py.)
     396
    367397Passing extra options to view functions
    368398=======================================
    369399
  • tests/regressiontests/urlpatterns/urls3.py

     
     1from django.conf.urls.defaults import *
     2
     3urlpatterns = patterns('regressiontests.urlpatterns.tests',
     4    (r'^test/$','view'),
     5)
     6 No newline at end of file
  • tests/regressiontests/urlpatterns/tests.py

     
     1"Unit tests for URL lookup"
     2
     3from django.conf import settings
     4from django.core.urlresolvers import resolve
     5import unittest
     6
     7def view(request):
     8    pass
     9
     10class URLPattern(unittest.TestCase):
     11    def setUp(self):
     12        self._url_conf = settings.ROOT_URLCONF
     13        settings.ROOT_URLCONF = 'regressiontests.urlpatterns.urls1'
     14       
     15    def tearDown(self):
     16        settings.ROOT_URLCONF = self._url_conf
     17       
     18    def test_urlpattern_1(self):
     19        result = resolve('/test/')
     20        self.assertEqual('stem' in result[2], False)
     21
     22    def test_urlpattern_2(self):
     23        result = resolve('/include/test/')
     24        self.assertEqual('stem' in result[2], True)
     25        self.assertEqual(result[2]['stem'],'/include/')
     26
     27    def test_urlpattern_3(self):
     28        result = resolve('/include/include/test/')
     29        self.assertEqual('stem' in result[2], True)
     30        self.assertEqual(result[2]['stem'],'/include/include/')
  • tests/regressiontests/urlpatterns/urls1.py

     
     1from django.conf.urls.defaults import *
     2
     3urlpatterns = patterns('regressiontests.urlpatterns.tests',
     4    (r'^test/$','view'),
     5    (r'^include/',include('regressiontests.urlpatterns.urls2')),
     6)
     7 No newline at end of file
  • tests/regressiontests/urlpatterns/urls2.py

     
     1from django.conf.urls.defaults import *
     2
     3urlpatterns = patterns('regressiontests.urlpatterns.tests',
     4    (r'^test/$','view',{'stem':stem}),
     5    (r'^include/',include('regressiontests.urlpatterns.urls3'),{'stem':stem}),
     6)
     7 No newline at end of file
  • tests/regressiontests/urlpatterns/tests.py

     
     1"Unit tests for URL lookup"
     2
     3from django.conf import settings
     4from django.core.urlresolvers import resolve
     5import unittest
     6
     7def view(request):
     8    pass
     9
     10class URLPattern(unittest.TestCase):
     11    def setUp(self):
     12        self._url_conf = settings.ROOT_URLCONF
     13        settings.ROOT_URLCONF = 'regressiontests.urlpatterns.urls1'
     14       
     15    def tearDown(self):
     16        settings.ROOT_URLCONF = self._url_conf
     17       
     18    def test_urlpattern_1(self):
     19        result = resolve('/test/')
     20        self.assertEqual('stem' in result[2], False)
     21
     22    def test_urlpattern_2(self):
     23        result = resolve('/include/test/')
     24        self.assertEqual('stem' in result[2], True)
     25        self.assertEqual(result[2]['stem'],'/include/')
     26
     27    def test_urlpattern_3(self):
     28        result = resolve('/include/include/test/')
     29        self.assertEqual('stem' in result[2], True)
     30        self.assertEqual(result[2]['stem'],'/include/include/')
  • tests/regressiontests/urlpatterns/urls1.py

     
     1from django.conf.urls.defaults import *
     2
     3urlpatterns = patterns('regressiontests.urlpatterns.tests',
     4    (r'^test/$','view'),
     5    (r'^include/',include('regressiontests.urlpatterns.urls2')),
     6)
     7 No newline at end of file
  • tests/regressiontests/urlpatterns/urls2.py

     
     1from django.conf.urls.defaults import *
     2
     3urlpatterns = patterns('regressiontests.urlpatterns.tests',
     4    (r'^test/$','view',{'stem':stem}),
     5    (r'^include/',include('regressiontests.urlpatterns.urls3'),{'stem':stem}),
     6)
     7 No newline at end of file
  • tests/regressiontests/urlpatterns/urls3.py

     
     1from django.conf.urls.defaults import *
     2
     3urlpatterns = patterns('regressiontests.urlpatterns.tests',
     4    (r'^test/$','view'),
     5)
     6 No newline at end of file
Back to Top