Combining Traversal and URL Dispatch
When you write most :app:`Pyramid` applications, you'll be using one or the other of two available :term:`context finding` subsystems: traversal or URL dispatch. However, to solve a limited set of problems, it's useful to use both traversal and URL dispatch together within the same application. :app:`Pyramid` makes this possible via hybrid applications.
Reasoning about the behavior of a "hybrid" URL dispatch + traversal application can be challenging. To successfully reason about using URL dispatch and traversal together, you need to understand URL pattern matching, root factories, and the :term:`traversal` algorithm, and the potential interactions between them. Therefore, we don't recommend creating an application that relies on hybrid behavior unless you must.
A Review of Non-Hybrid Applications
When used according to the tutorials in its documentation :app:`Pyramid` is a "dual-mode" framework: the tutorials explain how to create an application in terms of using either :term:`url dispatch` or :term:`traversal`. This chapter details how you might combine these two dispatch mechanisms, but we'll review how they work in isolation before trying to combine them.
URL Dispatch Only
An application that uses :term:`url dispatch` exclusively to map URLs to code will often have statements like this within your application startup configuration:
Each :term:`route` typically corresponds to a single view callable,
and when that route is matched during a request, the view callable
named by the
view attribute is invoked.
Typically, an application that uses only URL dispatch won't perform any calls to :meth:`pyramid.configuration.Configurator.add_view` in its startup code.
An application that uses only traversal will have view configuration declarations that look like this:
When the above configuration is applied to an application, the
mypackage.views.foobar view callable above will be called when the URL
/foobar is visited. Likewise, the view
be called when the URL
/bazbuz is visited.
Typically, an application that uses traversal exclusively won't perform any calls to :meth:`pyramid.configuration.Configurator.add_route` in its startup code.
Either traversal or url dispatch alone can be used to create a :app:`Pyramid` application. However, it is also possible to combine the concepts of traversal and url dispatch when building an application: the result is a hybrid application. In a hybrid application, traversal is performed after a particular route has matched.
A hybrid application is a lot more like a "pure" traversal-based
application than it is like a "pure" URL-dispatch based application.
But unlike in a "pure" traversal-based application, in a hybrid
application, :term:`traversal` is performed during a request after a
route has already matched. This means that the URL pattern that
pattern argument of a route must match the
PATH_INFO of a request, and after the route pattern has matched,
most of the "normal" rules of traversal with respect to :term:`context
finding` and :term:`view lookup` apply.
There are only four real differences between a purely traversal-based application and a hybrid application:
- In a purely traversal based application, no routes are defined; in a hybrid application, at least one route will be defined.
- In a purely traversal based application, the root object used is global implied by the :term:`root factory` provided at startup time; in a hybrid application, the :term:`root` object at which traversal begins may be varied on a per-route basis.
- In a purely traversal-based application, the
PATH_INFOof the underlying :term:`WSGI` environment is used wholesale as a traversal path; in a hybrid application, the traversal path is not the entire
PATH_INFOstring, but a portion of the URL determined by a matching pattern in the matched route configuration's pattern.
- In a purely traversal based application, view configurations which
do not mention a
route_nameargument are considered during :term:`view lookup`; in a hybrid application, when a route is matched, only view configurations which mention that route's name as a
route_nameare considered during :term:`view lookup`.
More generally, a hybrid application is a traversal-based application except:
- the traversal root is chosen based on the route configuration of
the route that matched instead of from the
root_factorysupplied during application startup configuration.
- the traversal path is chosen based on the route configuration of
the route that matched rather than from the
PATH_INFOof a request.
- the set of views that may be chosen during :term:`view lookup` when
a route matches are limited to those which specifically name a
route_namein their configuration that is the same as the matched route's
To create a hybrid mode application, use a :term:`route configuration`
that implies a particular :term:`root factory` and which also includes
pattern argument that contains a special dynamic part: either
The Root Object for a Route Match
A hybrid application implies that traversal is performed during a request after a route has matched. Traversal, by definition, must always begin at a root object. Therefore it's important to know which root object will be traversed after a route has matched.
Figuring out which :term:`root` object results from a particular route match is straightforward. When a route is matched:
- If the route's configuration has a
factoryargument which points to a :term:`root factory` callable, that callable will be called to generate a :term:`root` object.
- If the route's configuration does not have a
factoryargument, the global :term:`root factory` will be called to generate a :term:`root` object. The global root factory is the callable implied by the
root_factoryargument passed to :class:`pyramid.configuration.Configurator` at application startup time.
- If a
root_factoryargument is not provided to the :class:`pyramid.configuration.Configurator` at startup time, a default root factory is used. The default root factory is used to generate a root object.
Root factories related to a route were explained previously within :ref:`route_factories`. Both the global root factory and default root factory were explained previously within :ref:`the_object_graph`.
*traverse In a Route Pattern
A hybrid application most often implies the inclusion of a route
configuration that contains the special token
*traverse at the end
of a route's pattern:
*traverse token at the end of the pattern in a route's
configuration implies a "remainder" capture value. When it is used,
it will match the remainder of the path segments of the URL. This
remainder becomes the path used to perform traversal.
*remainder route pattern syntax is explained in more
detail within :ref:`route_pattern_syntax`.
Note that unlike the examples provided within :ref:`urldispatch_chapter`, the
add_route configuration statement named previously does not pass a
view argument. This is because a hybrid mode application relies on
:term:`traversal` to do :term:`context finding` and :term:`view lookup`
instead of invariably invoking a specific view callable named directly within
the matched route's configuration.
Because the pattern of the above route ends with
*traverse, when this
route configuration is matched during a request, :app:`Pyramid`
will attempt to use :term:`traversal` against the :term:`root` object
implied by the :term:`root factory` implied by the route's
configuration. Once :term:`traversal` has found a :term:`context`,
:term:`view lookup` will be invoked in almost exactly the same way it
would have been invoked in a "pure" traversal-based application.
The default :term:`root factory` cannot be traversed: it has no
__getitem__ method. So we'll need to associate this route
configuration with a non-default root factory in order to create a
useful hybrid application. To that end, let's imagine that we've
created a root factory that looks like so in a module named
Above, we've defined a (bogus) graph here that can be traversed, and a
root_factory function that can be used as part of a particular
route configuration statement:
factory above points at the function we've defined. It
will return an instance of the
Traversable class as a root object
whenever this route is matched. Because the
we've defined has a
__getitem__ method that does something
nominally useful, and because traversal uses
__getitem__ to walk
the nodes that make up an object graph, using traversal against the
root object implied by our route statement becomes a reasonable thing
We could have also used our
root_factory callable as the
root_factory argument of the
:class:`pyramid.configuration.Configurator` constructor instead
of associating it with a particular route inside the route's
configuration. Every hybrid route configuration that is matched but
which does not name a
factory` attribute will use the use
root_factory function to generate a root object.
When the route configuration named
home above is matched during a
request, the matchdict generated will be based on its pattern:
:foo/:bar/*traverse. The "capture value" implied by the
*traverse element in the pattern will be used to traverse the
graph in order to find a context, starting from the root object
returned from the root factory. In the above example, the
:term:`root` object found will be the instance named
If the URL that matched a route with the pattern
http://example.com/one/two/a/b/c, the traversal path used
against the root object will be
a/b/c. As a result,
:app:`Pyramid` will attempt to traverse through the edges
c, beginning at the root object.
In our above example, this particular set of traversal steps will mean
that the :term:`context` of the view would be the
object we've named
c in our bogus graph and the :term:`view name`
resulting from traversal will be the empty string; if you need a
refresher about why this outcome is presumed, see
At this point, a suitable view callable will be found and invoked using :term:`view lookup` as described in :ref:`view_configuration`, but with a caveat: in order for view lookup to work, we need to define a view configuration that will match when :term:`view lookup` is invoked after a route matches:
Note that the above call to
:meth:`pyramid.configuration.Configurator.add_view` includes a
argument. View configurations that include a
route_name argument are
meant to associate a particular view declaration with a route, using the
route's name, in order to indicate that the view should only be invoked when
the route matches.
Calls to :meth:`pyramid.configuration.Configurator.add_view` may pass a
route_name attribute which refers to the value of an existing route's
name argument. In the above example, the route name is
referring to the name of the route defined above it.
mypackage.views.myview view callable will be invoked when:
- the route named "home" is matched
- the :term:`view name` resulting from traversal is the empty string.
- the :term:`context` is any object.
It is also possible to declare alternate views that may be invoked when a hybrid route is matched:
add_view call for
mypackage.views.another_view above names a
different view and, more importantly, a different :term:`view name`. The
mypackage.views.another_view view will be invoked when:
- the route named "home" is matched
- the :term:`view name` resulting from traversal is
- the :term:`context` is any object.
For instance, if the URL
http://example.com/one/two/a/another is provided
to an application that uses the previously mentioned object graph, the
mypackage.views.another view callable will be called instead of the
mypackage.views.myview view callable because the :term:`view name` will
another instead of the empty string.
More complicated matching can be composed. All arguments to route configuration statements and view configuration statements are supported in hybrid applications (such as :term:`predicate` arguments).
traverse Argument In a Route Definition
Rather than using the
*traverse remainder marker in a pattern, you
can use the
traverse argument to the
When you use the
*traverse remainder marker, the traversal path is
limited to being the remainder segments of a request URL when a route
matches. However, when you use the
traverse argument or
attribute, you have more control over how to compose a traversal path.
Here's a use of the
traverse pattern in a call to
The syntax of the
traverse argument is the same as it is for
If, as above, the
pattern provided is
traverse argument provided is
/:article, when a
request comes in that causes the route to match in such a way that the
article match value is
1 (when the request URI is
/articles/1/edit), the traversal path will be generated as
This means that the root object's
__getitem__ will be called with
1 during the traversal phase. If the
exists, it will become the :term:`context` of the request.
:ref:`traversal_chapter` has more information about traversal.
If the traversal path contains segment marker names which are not
present in the pattern argument, a runtime error will occur. The
traverse pattern should not contain segment markers that do not
exist in the
Note that the
traverse argument is ignored when attached to a
route that has a
*traverse remainder marker in its pattern.
Traversal will begin at the root object implied by this route (either
the global root, or the object returned by the
with this route).
Making Global Views Match
By default, view configurations that don't mention a
route_name will be
not found by view lookup when a route that mentions a
*traverse in its
pattern matches. You can make these match forcibly by adding the
use_global_views flag to the route definition. For example, the
myproject.views.bazbuz view below will be found if the route named
abc below is matched and the
though the view configuration statement does not have the
*subpath in a Route Pattern
There are certain extremely rare cases when you'd like to influence
the traversal :term:`subpath` when a route matches without actually
performing traversal. For instance, the
:func:`pyramid.wsgi.wsgiapp2` decorator and the
:class:`pyramid.view.static` helper attempt to compute
PATH_INFO from the request's subpath, so it's useful to be able to
influence this value.
*subpath exists in a pattern, no path is actually traversed,
but the traversal algorithm will return a :term:`subpath` list implied
by the capture value of
*subpath. You'll see this pattern most
commonly in route declarations that look like this:
mypackage.views.static_view is an instance of
:class:`pyramid.view.static`. This effectively tells the static helper to
traverse everything in the subpath as a filename.
A number of corner case "gotchas" exist when using a hybrid application. We'll detail them here.
Registering a Default View for a Route That Has a
It is an error to provide both a
view argument to a :term:`route
configuration` and a :term:`view configuration` which names a
route_name that has no
name value or the empty
name value. For
example, this pair of declarations will generate a "conflict" error at
This is because the
view argument to the
:meth:`pyramid.configuration.Configurator.add_route` above is an implicit
default view when that route matches.
add_route calls don't need to
supply a view attribute. For example, this
Can also be spelled like so:
The two spellings are logically equivalent. In fact, the former is just a syntactical shortcut for the latter.
Binding Extra Views Against a Route Configuration that Doesn't Have a
*traverse Element In Its Patttern
Here's another corner case that just makes no sense.
The above view declaration is useless, because it will never be matched when
the route it references has matched. Only the view associated with the route
myproject.views.abc) will ever be invoked when the route matches,
because the default view is always invoked when a route matches and when no
post-match traversal is performed.
To make the above view declaration non-useless, the special
token must end the route's pattern. For example: