summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2009-06-18 06:35:21 +0000
committerChris McDonough <chrism@agendaless.com>2009-06-18 06:35:21 +0000
commitf8dbdee6167b3b4ab1ee4b2138a3e04e47a7c9df (patch)
tree2c03802fe9b3ab56d8e8bd067e437d3f29b92006
parent95a9cfc326f0fcb3bdfce1efe5c25748a7f8f077 (diff)
downloadpyramid-f8dbdee6167b3b4ab1ee4b2138a3e04e47a7c9df.tar.gz
pyramid-f8dbdee6167b3b4ab1ee4b2138a3e04e47a7c9df.tar.bz2
pyramid-f8dbdee6167b3b4ab1ee4b2138a3e04e47a7c9df.zip
- The matchdict related to the matching of a Routes route is available
on the request as the ``matchdict`` attribute: ``request.matchdict``. If no route matched, this attribute will be None.
-rw-r--r--CHANGES.txt5
-rw-r--r--docs/tutorials/bfgwiki2/authorization.rst2
-rw-r--r--docs/tutorials/bfgwiki2/basiclayout.rst24
-rw-r--r--docs/tutorials/bfgwiki2/definingviews.rst63
-rw-r--r--docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml10
-rw-r--r--docs/tutorials/bfgwiki2/src/authorization/tutorial/views.py9
-rw-r--r--docs/tutorials/bfgwiki2/src/basiclayout/tutorial/configure.zcml2
-rw-r--r--docs/tutorials/bfgwiki2/src/models/tutorial/configure.zcml2
-rw-r--r--docs/tutorials/bfgwiki2/src/views/tutorial/configure.zcml2
-rw-r--r--docs/tutorials/bfgwiki2/src/views/tutorial/views.py9
-rw-r--r--repoze/bfg/tests/test_traversal.py11
-rw-r--r--repoze/bfg/traversal.py12
12 files changed, 96 insertions, 55 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 4ce3369a4..2851f61ad 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -4,6 +4,11 @@ Next release
Features
--------
+- The matchdict related to the matching of a Routes route is available
+ on the request as the ``matchdict`` attribute:
+ ``request.matchdict``. If no route matched, this attribute will be
+ None.
+
- Make 404 responses slightly cheaper by showing
``environ["PATH_INFO"]`` on the notfound result page rather than the
fullly computed URL.
diff --git a/docs/tutorials/bfgwiki2/authorization.rst b/docs/tutorials/bfgwiki2/authorization.rst
index 402e42f8d..1e1fce92c 100644
--- a/docs/tutorials/bfgwiki2/authorization.rst
+++ b/docs/tutorials/bfgwiki2/authorization.rst
@@ -1,3 +1,5 @@
+.. _wiki2_adding_authorization:
+
====================
Adding Authorization
====================
diff --git a/docs/tutorials/bfgwiki2/basiclayout.rst b/docs/tutorials/bfgwiki2/basiclayout.rst
index 98e8bb183..303622c7e 100644
--- a/docs/tutorials/bfgwiki2/basiclayout.rst
+++ b/docs/tutorials/bfgwiki2/basiclayout.rst
@@ -32,16 +32,24 @@ XML namespace. Our sample ZCML file looks like the following:
SQLAlchemy connection after a request is finished.
#. *Lines 9-12*. Register a ``<route>`` that will be used when the
- URL is ``/``. Since this ``<view>`` has a blank ``name``
- attribute, it is the "default" view. ``.views.my_view`` refers to a
+ URL is ``/``. Since this ``<route>`` has an empty ``path``
+ attribute, it is the "default" route. The attribute named ``view``
+ with the value ``.views.my_view`` is the dotted name to a
*function* we write (generated by the ``bfg_routesalchemy``
template) that is given a ``context`` and a ``request`` and returns
- a response.
-
-#. *Lines 14-17*. Register a ``<route>`` with a path that starts with
- ``/static``, capturing the rest of the URL as ``subpath``. This is
- a view that will serve up static resources for us, in this case, at
- ``http://localhost:6543/static/`` and below.
+ a response. You will use mostly ``<route>`` statements in a
+ :term:`URL dispatch` based application to map URLs to code.
+
+#. *Lines 14-17*. Register a ``<view>`` with a path that starts with
+ ``/static``. This points at a bit of code (``.views.static_view``)
+ that will serve up static resources for us, in this case, at
+ ``http://localhost:6543/static/`` and below. ``<view>``
+ declarations also map code to URLs like ``route`` statements,
+ except they match URLs based on :term:`traversal`. With this view
+ declaration, we're saying that any URL that starts with ``/static``
+ should go to the static view; any remainder of its path (e.g. the
+ ``/foo`` in ``/static/foo``) will be used to compose a path to a
+ static file resource (CSS and such).
Content Models with ``models.py``
---------------------------------
diff --git a/docs/tutorials/bfgwiki2/definingviews.rst b/docs/tutorials/bfgwiki2/definingviews.rst
index 4d90db11f..25f52291c 100644
--- a/docs/tutorials/bfgwiki2/definingviews.rst
+++ b/docs/tutorials/bfgwiki2/definingviews.rst
@@ -7,13 +7,20 @@ parameters: :term:`context`, and :term:`request`. A view is assumed
to return a :term:`response` object.
A invocation of a view that matches a URL via :term:`url dispatch`
-passes as the context object an object which has attributes matching
-the elements placed into the URL by the ``path`` of a ``route``
-statement. For instance, if a route statement in ``configure.zcml``
-had the path ``:one/:two``, and the URL at
-``http://example.com/foo/bar`` was invoked, matching this path, the
-context object passed to the view would have a ``one`` attribute withe
-the value ``foo`` and a ``two`` attribute with the value ``bar``.
+passes as the context object an object generated by a ``root
+factory``. In this application, the context will always be generated
+by the *default* root factory. Since we're not using traversal in
+this application, this means the context will appear useless, at least
+until we get to :ref:`wiki2_adding_authorization`.
+
+The request passed to every view that is called as the result of a
+route match has an attribute named ``matchdict`` that contains the
+elements placed into the URL by the ``path`` of a ``route`` statement.
+For instance, if a route statement in ``configure.zcml`` had the path
+``:one/:two``, and the URL at ``http://example.com/foo/bar`` was
+invoked, matching this path, the matchdict dictionary attached to the
+request passed to the view would have a ``one`` key withe the value
+``foo`` and a ``two`` key with the value ``bar``.
Declaring Dependencies in Our ``setup.py`` File
===============================================
@@ -111,14 +118,14 @@ The ``add_page`` function will be invoked when a user clicks on a
*WikiWord* which isn't yet represented as a page in the system. The
``check`` function within the ``view_page`` view generates URLs to
this view. It also acts as a handler for the form that is generated
-when we want to add a page object. The ``context`` of the
-``add_page`` view will always have the attributes we need to
-construct URLs and find model objects.
+when we want to add a page object. The ``matchdict`` attribute of the
+request passed to the ``add_page`` view will have the values we need
+to construct URLs and find model objects.
-The context object will have a ``pagename`` attribute that matches the
-name of the page we'd like to add. If our add view is invoked via,
+The matchdict will have a ``pagename`` key that matches the name of
+the page we'd like to add. If our add view is invoked via,
e.g. ``http://localhost:6543/add_page/SomeName``, the ``pagename``
-attribute of the context will be ``SomeName``.
+value in the matchdict will be ``SomeName``.
If the view rendering is *not* a result of a form submission (if the
expression ``'form.submitted' in request.params`` is False), the view
@@ -133,7 +140,7 @@ response.
If the view rendering *is* a result of a form submission (if the
expression ``'form.submitted' in request.params`` is True), we scrape
the page body from the form data, create a Page object using the name
-in ``pagename`` context attribute and obtain the page body from the
+in the matchdict ``pagename``, and obtain the page body from the
request, and save it into the database using ``session.add``. We then
redirect back to the ``view_page`` view (the default view for a page)
for the newly created page.
@@ -143,9 +150,10 @@ The ``edit_page`` view function
The ``edit_page`` function will be invoked when a user clicks the
"Edit this Page" button on the view form. It renders an edit form but
-it also acts as the handler for the form it renders. The ``context``
-of the ``edit_page`` view will *always* have a ``pagename`` attribute
-matching the name of the page the user wants to edit.
+it also acts as the handler for the form it renders. The
+``matchdict`` attribute of the request passed to the ``add_page`` view
+will have a ``pagename`` key matching the name of the page the user
+wants to edit.
If the view rendering is *not* a result of a form submission (if the
expression ``'form.submitted' in request.params`` is False), the view
@@ -155,9 +163,9 @@ and a save_url which will be used as the action of the generated form.
If the view rendering *is* a result of a form submission (if the
expression ``'form.submitted' in request.params`` is True), the view
grabs the ``body`` element of the request parameter and sets it as the
-``data`` attribute of the page context. It then redirects to the
-default view of the context (the page), which will always be the
-``view_page`` view.
+``data`` key in the matchdict. It then redirects to the default view
+of the context (the page), which will always be the ``view_page``
+view.
Viewing the Result of Our Edits to ``views.py``
===============================================
@@ -234,14 +242,15 @@ number and type of static resources can be placed in this directory
Mapping Views to URLs in ``configure.zcml``
===========================================
-The ``configure.zcml`` file contains ``route`` declarations which
-serve to map URLs (via :term:`url dispatch`) to view functions.
-First, we’ll get rid of the existing ``route`` created by the
-template using the name ``home``. It’s only an example and isn’t
-relevant to our application. We'll leave the static ``route``
-declaration as it is, since we are going to use it for the CSS.
+The ``configure.zcml`` file contains ``route`` declarations (and a
+lone ``view`` declaration) which serve to map URLs (via :term:`url
+dispatch`) to view functions. First, we’ll get rid of the existing
+``route`` created by the template using the name ``home``. It’s only
+an example and isn’t relevant to our application. We'll leave the
+static ``view`` declaration as it is, since we are going to use it to
+serve CSS.
-We then need to add four ``view`` declarations to ``configure.zcml``.
+We then need to add four ``route`` declarations to ``configure.zcml``.
Note that the *ordering* of these declarations is very important.
``route`` declarations are matched in the order they're found in the
``configure.zcml`` file.
diff --git a/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml b/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml
index ff0125f83..fd8c8484c 100644
--- a/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml
+++ b/docs/tutorials/bfgwiki2/src/authorization/tutorial/configure.zcml
@@ -6,6 +6,11 @@
<subscriber for="repoze.bfg.interfaces.INewRequest"
handler=".run.handle_teardown"/>
+ <view
+ name="static"
+ view=".views.static_view"
+ />
+
<route
path="login"
name="login"
@@ -18,11 +23,6 @@
view=".login.logout"
/>
- <route path="static/*subpath"
- name="static"
- view=".views.static_view"
- />
-
<route
path=""
name="view_wiki"
diff --git a/docs/tutorials/bfgwiki2/src/authorization/tutorial/views.py b/docs/tutorials/bfgwiki2/src/authorization/tutorial/views.py
index b38a9489e..4bcaae185 100644
--- a/docs/tutorials/bfgwiki2/src/authorization/tutorial/views.py
+++ b/docs/tutorials/bfgwiki2/src/authorization/tutorial/views.py
@@ -22,8 +22,9 @@ def view_wiki(context, request):
return HTTPFound(location = url_for('view_page', pagename='FrontPage'))
def view_page(context, request):
+ pagename = request.matchdict['pagename']
session = DBSession()
- page = session.query(Page).filter_by(name=context.pagename).one()
+ page = session.query(Page).filter_by(name=pagename).one()
def check(match):
word = match.group(1)
@@ -37,7 +38,7 @@ def view_page(context, request):
content = publish_parts(page.data, writer_name='html')['html_body']
content = wikiwords.sub(check, content)
- edit_url = url_for('edit_page', pagename=context.pagename)
+ edit_url = url_for('edit_page', pagename=pagename)
logged_in = authenticated_userid(request)
return render_template_to_response('templates/view.pt',
request = request,
@@ -47,7 +48,7 @@ def view_page(context, request):
edit_url = edit_url)
def add_page(context, request):
- name = context.pagename
+ name = request.matchdict['pagename']
if 'form.submitted' in request.params:
session = DBSession()
body = request.params['body']
@@ -64,7 +65,7 @@ def add_page(context, request):
save_url = save_url)
def edit_page(context, request):
- name = context.pagename
+ name = request.matchdict['pagename']
session = DBSession()
page = session.query(Page).filter_by(name=name).one()
if 'form.submitted' in request.params:
diff --git a/docs/tutorials/bfgwiki2/src/basiclayout/tutorial/configure.zcml b/docs/tutorials/bfgwiki2/src/basiclayout/tutorial/configure.zcml
index 4858c47fd..4d1a16612 100644
--- a/docs/tutorials/bfgwiki2/src/basiclayout/tutorial/configure.zcml
+++ b/docs/tutorials/bfgwiki2/src/basiclayout/tutorial/configure.zcml
@@ -11,7 +11,7 @@
view=".views.my_view"
/>
- <route path="static/*subpath"
+ <view
name="static"
view=".views.static_view"
/>
diff --git a/docs/tutorials/bfgwiki2/src/models/tutorial/configure.zcml b/docs/tutorials/bfgwiki2/src/models/tutorial/configure.zcml
index 4858c47fd..4d1a16612 100644
--- a/docs/tutorials/bfgwiki2/src/models/tutorial/configure.zcml
+++ b/docs/tutorials/bfgwiki2/src/models/tutorial/configure.zcml
@@ -11,7 +11,7 @@
view=".views.my_view"
/>
- <route path="static/*subpath"
+ <view
name="static"
view=".views.static_view"
/>
diff --git a/docs/tutorials/bfgwiki2/src/views/tutorial/configure.zcml b/docs/tutorials/bfgwiki2/src/views/tutorial/configure.zcml
index 70c3d4d9a..9d936c6d3 100644
--- a/docs/tutorials/bfgwiki2/src/views/tutorial/configure.zcml
+++ b/docs/tutorials/bfgwiki2/src/views/tutorial/configure.zcml
@@ -6,7 +6,7 @@
<subscriber for="repoze.bfg.interfaces.INewRequest"
handler=".run.handle_teardown"/>
- <route path="static/*subpath"
+ <view
name="static"
view=".views.static_view"
/>
diff --git a/docs/tutorials/bfgwiki2/src/views/tutorial/views.py b/docs/tutorials/bfgwiki2/src/views/tutorial/views.py
index c4e91dda1..f81fbf2af 100644
--- a/docs/tutorials/bfgwiki2/src/views/tutorial/views.py
+++ b/docs/tutorials/bfgwiki2/src/views/tutorial/views.py
@@ -21,8 +21,9 @@ def view_wiki(context, request):
return HTTPFound(location = url_for('view_page', pagename='FrontPage'))
def view_page(context, request):
+ matchdict = request.matchdict
session = DBSession()
- page = session.query(Page).filter_by(name=context.pagename).one()
+ page = session.query(Page).filter_by(name=matchdict['pagename']).one()
def check(match):
word = match.group(1)
@@ -36,7 +37,7 @@ def view_page(context, request):
content = publish_parts(page.data, writer_name='html')['html_body']
content = wikiwords.sub(check, content)
- edit_url = url_for('edit_page', pagename=context.pagename)
+ edit_url = url_for('edit_page', pagename=matchdict['pagename'])
return render_template_to_response('templates/view.pt',
request = request,
page = page,
@@ -44,7 +45,7 @@ def view_page(context, request):
edit_url = edit_url)
def add_page(context, request):
- name = context.pagename
+ name = request.matchdict['pagename']
if 'form.submitted' in request.params:
session = DBSession()
body = request.params['body']
@@ -59,7 +60,7 @@ def add_page(context, request):
save_url = save_url)
def edit_page(context, request):
- name = context.pagename
+ name = request.matchdict['pagename']
session = DBSession()
page = session.query(Page).filter_by(name=name).one()
if 'form.submitted' in request.params:
diff --git a/repoze/bfg/tests/test_traversal.py b/repoze/bfg/tests/test_traversal.py
index 00b3cc99f..a36cbb1dc 100644
--- a/repoze/bfg/tests/test_traversal.py
+++ b/repoze/bfg/tests/test_traversal.py
@@ -90,6 +90,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
self.assertEqual(result['traversed'], [])
self.assertEqual(result['virtual_root'], policy.root)
self.assertEqual(result['virtual_root_path'], [])
+ self.assertEqual(result['matchdict'], None)
def test_call_pathel_with_no_getitem(self):
policy = self._makeOne(None)
@@ -101,6 +102,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
self.assertEqual(result['traversed'], [])
self.assertEqual(result['virtual_root'], policy.root)
self.assertEqual(result['virtual_root_path'], [])
+ self.assertEqual(result['matchdict'], None)
def test_call_withconn_getitem_emptypath_nosubpath(self):
root = DummyContext()
@@ -113,6 +115,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
self.assertEqual(result['traversed'], [])
self.assertEqual(result['virtual_root'], root)
self.assertEqual(result['virtual_root_path'], [])
+ self.assertEqual(result['matchdict'], None)
def test_call_withconn_getitem_withpath_nosubpath(self):
foo = DummyContext()
@@ -126,6 +129,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
self.assertEqual(result['traversed'], [u'foo'])
self.assertEqual(result['virtual_root'], root)
self.assertEqual(result['virtual_root_path'], [])
+ self.assertEqual(result['matchdict'], None)
def test_call_withconn_getitem_withpath_withsubpath(self):
foo = DummyContext()
@@ -139,6 +143,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
self.assertEqual(result['traversed'], [u'foo'])
self.assertEqual(result['virtual_root'], root)
self.assertEqual(result['virtual_root_path'], [])
+ self.assertEqual(result['matchdict'], None)
def test_call_with_explicit_viewname(self):
foo = DummyContext()
@@ -152,6 +157,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
self.assertEqual(result['traversed'], [])
self.assertEqual(result['virtual_root'], root)
self.assertEqual(result['virtual_root_path'], [])
+ self.assertEqual(result['matchdict'], None)
def test_call_with_vh_root(self):
environ = self._getEnviron(PATH_INFO='/baz',
@@ -172,6 +178,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
self.assertEqual(result['traversed'], [u'foo', u'bar', u'baz'])
self.assertEqual(result['virtual_root'], bar)
self.assertEqual(result['virtual_root_path'], [u'foo', u'bar'])
+ self.assertEqual(result['matchdict'], None)
def test_non_utf8_path_segment_unicode_path_segments_fails(self):
foo = DummyContext()
@@ -201,6 +208,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
self.assertEqual(result['traversed'], [])
self.assertEqual(result['virtual_root'], model)
self.assertEqual(result['virtual_root_path'], [])
+ self.assertEqual(result['matchdict'], {})
def test_withroute_with_subpath(self):
model = DummyContext()
@@ -213,6 +221,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
self.assertEqual(result['traversed'], [])
self.assertEqual(result['virtual_root'], model)
self.assertEqual(result['virtual_root_path'], [])
+ self.assertEqual(result['matchdict'], {'subpath':'/a/b/c'})
def test_withroute_with_path_info(self):
model = DummyContext()
@@ -228,6 +237,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
self.assertEqual(result['virtual_root_path'], [])
self.assertEqual(environ['PATH_INFO'], '/foo/bar')
self.assertEqual(environ['SCRIPT_NAME'], '/a/b')
+ self.assertEqual(result['matchdict'], {'path_info':'foo/bar'})
def test_withroute_with_path_info_PATH_INFO_w_extra_slash(self):
model = DummyContext()
@@ -249,6 +259,7 @@ class ModelGraphTraverserTests(unittest.TestCase):
self.assertEqual(result['traversed'], [])
self.assertEqual(result['virtual_root'], model)
self.assertEqual(result['virtual_root_path'], [])
+ self.assertEqual(result['matchdict'], {'traverse':'foo/bar'})
class FindInterfaceTests(unittest.TestCase):
def _callFUT(self, context, iface):
diff --git a/repoze/bfg/traversal.py b/repoze/bfg/traversal.py
index 40f65901f..63fd71e74 100644
--- a/repoze/bfg/traversal.py
+++ b/repoze/bfg/traversal.py
@@ -493,6 +493,7 @@ class ModelGraphTraverser(object):
self.root = root
def __call__(self, environ, _marker=_marker):
+ matchdict = None
if 'bfg.routes.matchdict' in environ:
# this request matched a Routes route
matchdict = environ['bfg.routes.matchdict']
@@ -541,19 +542,22 @@ class ModelGraphTraverser(object):
if segment[:2] =='@@':
return dict(context=ob, view_name=segment[2:], subpath=path[i:],
traversed=traversed, virtual_root=vroot,
- virtual_root_path=vroot_path, root=self.root)
+ virtual_root_path=vroot_path, root=self.root,
+ matchdict=matchdict)
try:
getitem = ob.__getitem__
except AttributeError:
return dict(context=ob, view_name=segment, subpath=path[i:],
traversed=traversed, virtual_root=vroot,
- virtual_root_path=vroot_path, root=self.root)
+ virtual_root_path=vroot_path, root=self.root,
+ matchdict=matchdict)
try:
next = getitem(segment)
except KeyError:
return dict(context=ob, view_name=segment, subpath=path[i:],
traversed=traversed, virtual_root=vroot,
- virtual_root_path=vroot_path, root=self.root)
+ virtual_root_path=vroot_path, root=self.root,
+ matchdict=matchdict)
if vroot_idx == i-1:
vroot = ob
traversed.append(segment)
@@ -563,7 +567,7 @@ class ModelGraphTraverser(object):
return dict(context=ob, view_name=u'', subpath=subpath,
traversed=traversed, virtual_root=vroot,
virtual_root_path=vroot_path,
- root=self.root)
+ root=self.root, matchdict=matchdict)
class TraversalContextURL(object):
""" The IContextURL adapter used to generate URLs for a context