summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@plope.com>2011-01-03 03:10:15 -0500
committerChris McDonough <chrism@plope.com>2011-01-03 03:10:15 -0500
commit5653d13e554433adf34fd81b2c6593a54e7c4ea1 (patch)
tree4729e7ff7778d05c5d4f73818343b7bd70fb8b09
parent83373586ea80ea5057da5fc4ede43278af47a318 (diff)
downloadpyramid-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.txt3
-rw-r--r--TODO.txt2
-rw-r--r--docs/api/url.rst2
-rw-r--r--docs/glossary.rst5
-rw-r--r--pyramid/tests/test_config.py9
-rw-r--r--pyramid/tests/test_url.py42
-rw-r--r--pyramid/url.py74
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.
diff --git a/TODO.txt b/TODO.txt
index 0b2bacce2..15ce6ef05 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -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):