diff options
| author | Chris McDonough <chrism@plope.com> | 2011-01-03 03:10:15 -0500 |
|---|---|---|
| committer | Chris McDonough <chrism@plope.com> | 2011-01-03 03:10:15 -0500 |
| commit | 5653d13e554433adf34fd81b2c6593a54e7c4ea1 (patch) | |
| tree | 4729e7ff7778d05c5d4f73818343b7bd70fb8b09 | |
| parent | 83373586ea80ea5057da5fc4ede43278af47a318 (diff) | |
| download | pyramid-5653d13e554433adf34fd81b2c6593a54e7c4ea1.tar.gz pyramid-5653d13e554433adf34fd81b2c6593a54e7c4ea1.tar.bz2 pyramid-5653d13e554433adf34fd81b2c6593a54e7c4ea1.zip | |
- Add a new API ``pyramid.url.current_route_url``, which computes a URL based
on the "current" route (if any) and its matchdict values.
| -rw-r--r-- | CHANGES.txt | 3 | ||||
| -rw-r--r-- | TODO.txt | 2 | ||||
| -rw-r--r-- | docs/api/url.rst | 2 | ||||
| -rw-r--r-- | docs/glossary.rst | 5 | ||||
| -rw-r--r-- | pyramid/tests/test_config.py | 9 | ||||
| -rw-r--r-- | pyramid/tests/test_url.py | 42 | ||||
| -rw-r--r-- | pyramid/url.py | 74 |
7 files changed, 103 insertions, 34 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 8d0e50722..520a73847 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -15,6 +15,9 @@ Bug Fixes Features -------- +- Add a new API ``pyramid.url.current_route_url``, which computes a URL based + on the "current" route (if any) and its matchdict values. + - ``config.add_view`` now accepts a ``decorator`` keyword argument, a callable which will decorate the view callable before it is added to the registry. @@ -34,8 +34,6 @@ Must-Have (before 1.0) Should-Have ----------- -- ``current_route_url`` function. https://gist.github.com/762842 - - Convert paster template and tutorial HTML templates to use ``request.static_url('{{package}}:static/foo.css')`` rather than ``${request.application_url}/static/foo.css``. diff --git a/docs/api/url.rst b/docs/api/url.rst index 1aa3082b7..01be76283 100644 --- a/docs/api/url.rst +++ b/docs/api/url.rst @@ -9,6 +9,8 @@ .. autofunction:: route_url + .. autofunction:: current_route_url + .. autofunction:: route_path .. autofunction:: static_url diff --git a/docs/glossary.rst b/docs/glossary.rst index ce2d77d8d..890f7e837 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -857,3 +857,8 @@ Glossary view argument and return value mapping. This is a plug point for extension builders, not normally used by "civilians". + matchdict + The dictionary attached to the :term:`request` object as + ``request.matchdict`` when a :term:`URL dispatch` route has been matched. + Its keys are names as identified within the route pattern; its values are + the values matched by each pattern name. diff --git a/pyramid/tests/test_config.py b/pyramid/tests/test_config.py index 1632a4e5c..70e4df07e 100644 --- a/pyramid/tests/test_config.py +++ b/pyramid/tests/test_config.py @@ -3798,8 +3798,7 @@ class TestViewDeriver(unittest.TestCase): def wrapped(context, request): return 'OK' return wrapped - def view(context, request): - return 'NOTOK' + def view(context, request): return 'NOTOK' deriver = self._makeOne(view_mapper=mapper) result = deriver(view) self.failIf(result is view) @@ -3813,8 +3812,7 @@ class TestViewDeriver(unittest.TestCase): return 'OK' return superinner return inner - def view(context, request): - return 'NOTOK' + def view(context, request): return 'NOTOK' view.__view_mapper__ = mapper deriver = self._makeOne() result = deriver(view) @@ -3830,8 +3828,7 @@ class TestViewDeriver(unittest.TestCase): return superinner return inner self.config.set_view_mapper(mapper) - def view(context, request): - return 'NOTOK' + def view(context, request): return 'NOTOK' deriver = self._makeOne() result = deriver(view) self.failIf(result is view) diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py index f11d36aca..a40727e9b 100644 --- a/pyramid/tests/test_url.py +++ b/pyramid/tests/test_url.py @@ -209,6 +209,47 @@ class TestRouteUrl(unittest.TestCase): self.assertEqual(result, 'http://example2.com/1/2/3/a') self.assertEqual(route.kw, {}) # shouldnt have anchor/query +class TestCurrentRouteUrl(unittest.TestCase): + def setUp(self): + cleanUp() + + def tearDown(self): + cleanUp() + + def _callFUT(self, *arg, **kw): + from pyramid.url import current_route_url + return current_route_url(*arg, **kw) + + def test_current_request_has_no_route(self): + request = _makeRequest() + self.assertRaises(ValueError, self._callFUT, request) + + def test_with_elements_query_and_anchor(self): + from pyramid.interfaces import IRoutesMapper + request = _makeRequest() + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route=route) + request.matched_route = route + request.matchdict = {} + request.registry.registerUtility(mapper, IRoutesMapper) + result = self._callFUT(request, 'extra1', 'extra2', _query={'a':1}, + _anchor=u"foo") + self.assertEqual(result, + 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo') + + def test_with__route_name(self): + from pyramid.interfaces import IRoutesMapper + request = _makeRequest() + route = DummyRoute('/1/2/3') + mapper = DummyRoutesMapper(route=route) + request.matched_route = route + request.matchdict = {} + request.registry.registerUtility(mapper, IRoutesMapper) + result = self._callFUT(request, 'extra1', 'extra2', _query={'a':1}, + _anchor=u"foo", _route_name='bar') + self.assertEqual(result, + 'http://example.com:5432/1/2/3/extra1/extra2?a=1#foo') + class TestRoutePath(unittest.TestCase): def setUp(self): cleanUp() @@ -302,6 +343,7 @@ class DummyRoutesMapper: class DummyRoute: pregenerator = None + name = 'route' def __init__(self, result='/1/2/3'): self.result = result diff --git a/pyramid/url.py b/pyramid/url.py index cdeb0d6c8..3126ad26c 100644 --- a/pyramid/url.py +++ b/pyramid/url.py @@ -366,37 +366,59 @@ def static_url(path, request, **kw): return info.generate(path, request, **kw) -def current_route_url(request, *elements, **query): +def current_route_url(request, *elements, **kw): """Generates a fully qualified URL for a named :app:`Pyramid` - :term:`route configuration` based on current route. + :term:`route configuration` based on the 'current route'. - This function is to supplement :func:`pyramid.url.route_url`, it's purpose - is to allow for easy overriding parameters of current route. - - Example:: - If our url route is /foo/{page} - and current url is /foo/1 : - current_route_url(request, page='2') - Will return the string ``/foo/2``. + This function supplements :func:`pyramid.url.route_url`. It presents an + easy way to generate a URL for the 'current route' (defined as the route + which matched when the request was generated). + + The arguments to this function have the same meaning as those with the + same names passed to :func:`pyramid.url.route_url`. It also understands + an extra argument which ``route_url`` does not named ``_route_name``. + + The route name used to generate a URL is taken from either the + ``_route_name`` keyword argument or the name of the route which is + currently associated with the request if ``_route_name`` was not passed. + Keys and values from the current request :term:`matchdict` are combined + with the ``kw`` arguments to form a set of defaults named ``newkw``. + Then ``route_url(route_name, request, *elements, **newkw)`` is called, + returning a URL. + + Examples follow. + + If the 'current route' has the route pattern ``/foo/{page}`` and the + current url path is ``/foo/1`` , the matchdict will be ``{'page':'1'}``. + The result of ``current_route_url(request)`` in this situation will be + ``/foo/1``. + + If the 'current route' has the route pattern ``/foo/{page}`` and the + current current url path is ``/foo/1``, the matchdict will be + ``{'page':'1'}``. The result of ``current_route_url(request, page='2')`` + in this situation will be ``/foo/2``. - Alternatively we may have routes /foo/{action} and /foo/{action}/{page}, - on url /foo/view we may want to have a template macro/def that allows us - to output next/prev buttons that contain page numbers. The ability to - override route name helps with this task. - - Example:: - where ``foo_pages`` is route name for ``/foo/{action}/{page}`` - current_url(request, _route_name='foo_pages', page=paginator.page+1) - Will return the string like: ``/foo/view/5`` + Usage of the ``_route_name`` keyword argument: if our routing table + defines routes ``/foo/{action}`` named 'foo' and ``/foo/{action}/{page}`` + named ``fooaction``, and the current url pattern is ``/foo/view`` (which + has matched the ``/foo/{action}`` route), we may want to use the + matchdict args to generate a URL to the ``fooaction`` route. In this + scenario, ``current_url(request, _route_name='fooaction', page='5')`` + Will return string like: ``/foo/view/5``. """ - if '_route_name' in query: - route_name = query['_route_name'] + + if '_route_name' in kw: + route_name = kw.pop('_route_name') else: - route_name = getattr(request, 'matched_route', None) - route_name = getattr(route_name, 'name') - matchdict = {} - matchdict.update(getattr(request, 'matchdict', {}) or {}) - matchdict.update(query) - return route_url(route_name, request, *elements, **matchdict) + route = getattr(request, 'matched_route', None) + route_name = getattr(route, 'name', None) + if route_name is None: + raise ValueError('Current request matches no route') + + newkw = {} + newkw.update(request.matchdict) + newkw.update(kw) + return route_url(route_name, request, *elements, **newkw) @lru_cache(1000) def _join_elements(elements): |
