.. index::
single: URL dispatch
.. _urldispatch_chapter:
URL Dispatch
============
The URL dispatch feature of :mod:`pyramid` allows you to either
augment or replace :term:`traversal` as a :term:`context finding`
mechanism, allowing URL pattern matching to have the "first crack" at
resolving a given URL to :term:`context` and :term:`view name`.
Although it is a "context-finding" mechanism, ironically, using URL
dispatch exclusively allows you to avoid thinking about your
application in terms of "contexts" and "view names" entirely.
Many applications don't need :mod:`pyramid` features -- such as
declarative security via an :term:`authorization policy` -- that
benefit from having any visible separation between :term:`context
finding` and :term:`view lookup`. To this end, URL dispatch provides
a handy syntax that allows you to effectively map URLs *directly* to
:term:`view` code in such a way that you needn't think about your
application in terms of "context finding" at all. This makes developing
a :mod:`pyramid` application seem more like developing an
application in a system that is "context-free", such as :term:`Pylons`
or :term:`Django`.
Whether or not you care about "context", it often makes a lot of sense
to use :term:`URL dispatch` instead of :term:`traversal` in an
application that has no natural data hierarchy. For instance, if all
the data in your application lives in a relational database, and that
relational database has no self-referencing tables that form a natural
hierarchy, URL dispatch is easier to use than traversal, and is often
a more natural fit for creating an application that manipulates "flat"
data.
The presence of :ref:`route_directive` statements in a :term:`ZCML`
file used by your application or the presence of calls to the
:meth:`pyramid.configuration.Configurator.add_route` method in
imperative configuration within your application is a sign that you're
using :term:`URL dispatch`.
High-Level Operational Overview
-------------------------------
If route configuration is present in an application, the
:mod:`pyramid` :term:`Router` checks every incoming request against
an ordered set of URL matching patterns present in a *route map*.
If any route pattern matches the information in the :term:`request`
provided to :mod:`pyramid`, a route-specific :term:`context` and
:term:`view name` will be generated. In this circumstance,
:mod:`pyramid` will shortcut :term:`traversal`, and will invoke
:term:`view lookup` using the context and view name generated by URL
dispatch. If the matched route names a :term:`view callable` in its
configuration, that view callable will be invoked when view lookup is
performed.
However, if no route pattern matches the information in the
:term:`request` provided to :mod:`pyramid`, it will fail over to
using :term:`traversal` to perform context finding and view lookup.
Route Configuration
-------------------
:term:`Route configuration` is the act of adding a new :term:`route`
to an application. A route has a *pattern*, representing a pattern
meant to match against the ``PATH_INFO`` portion of a URL, and a
*name*, which is used by developers within a :mod:`pyramid`
application to uniquely identify a particular route when generating a
URL. It also optionally has a ``factory``, a set of :term:`route
predicate` parameters, and a set of :term:`view` parameters.
A route configuration may be added to the system via :term:`imperative
configuration` or via :term:`ZCML`. Both are completely equivalent.
.. index::
single: add_route
Configuring a Route Imperatively via The ``add_route`` Configurator Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The :meth:`pyramid.configuration.Configurator.add_route` method
adds a single :term:`route configuration` to the :term:`application
registry`. Here's an example:
.. ignore-next-block
.. code-block:: python
# "config" below is presumed to be an instance of the
# pyramid.configuration.Configurator class; "myview" is assumed
# to be a "view callable" function
from views import myview
config.add_route('myroute', '/prefix/:one/:two', view=myview)
.. index::
single: ZCML directive; route
Configuring a Route via ZCML
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Instead of using the imperative
:meth:`pyramid.configuration.Configurator.add_route` method to add
a new route, you can alternately use :term:`ZCML`. For example, the
following :term:`ZCML declaration` causes a route to be added to the
application.
.. code-block:: xml
:linenos:
.. note::
Values prefixed with a period (``.``) within the values of ZCML
attributes such as the ``view`` attribute of a ``route`` mean
"relative to the Python package directory in which this
:term:`ZCML` file is stored". So if the above ``route``
declaration was made inside a ``configure.zcml`` file that lived in
the ``hello`` package, you could replace the relative
``.views.myview`` with the absolute ``hello.views.myview`` Either
the relative or absolute form is functionally equivalent. It's
often useful to use the relative form, in case your package's name
changes. It's also shorter to type.
See :ref:`route_directive` for full ``route`` ZCML directive
documentation.
Route Configuration That Names a View Callable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When a route configuration declaration names a ``view`` attribute, the
value of the attribute will reference a :term:`view callable`. A view
callable, as described in :ref:`views_chapter`, is developer-supplied
code that "does stuff" as the result of a request. For more
information about how to create view callables, see
:ref:`views_chapter`.
Here's an example route configuration that references a view callable:
.. code-block:: xml
:linenos:
When a route configuration names a ``view`` attribute, the :term:`view
callable` named as that ``view`` attribute will always be found and
invoked when the associated route pattern matches during a request.
The purpose of making it possible to specify a view callable within a
route configuration is to prevent developers from needing to deeply
understand the details of :term:`context finding` and :term:`view
lookup`. When a route names a view callable, and a request enters the
system which matches the pattern of the route, the result is simple:
the view callable associated with the route is invoked with the
request that caused the invocation.
For most usage, you needn't understand more than this; how it works is
an implementation detail. In the interest of completeness, however,
we'll explain how it *does* work in the following section. You can
skip it if you're uninterested.
Route View Callable Registration and Lookup Details
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
When a ``view`` attribute is attached to a route configuration,
:mod:`pyramid` ensures that a :term:`view configuration` is
registered that will always be found when the route pattern is matched
during a request. To do so:
- A special route-specific :term:`interface` is created at startup time
for each route configuration declaration.
- When a route configuration declaration mentions a ``view``
attribute, a :term:`view configuration` is registered at startup
time. This view configuration uses the route-specific interface as
a :term:`request` type.
- At runtime, when a request causes any route to match, the
:term:`request` object is decorated with the route-specific
interface.
- The fact that the request is decorated with a route-specific
interface causes the view lookup machinery to always use the view
callable registered using that interface by the route configuration
to service requests that match the route pattern.
In this way, we supply a shortcut to the developer. Under the hood,
:mod:`pyramid` still consumes the :term:`context finding` and
:term:`view lookup` subsystems provided by :mod:`pyramid`, but in a
way which does not require that a developer understand either of them
if he doesn't want or need to. It also means that we can allow a
developer to combine :term:`URL dispatch` and :term:`traversal` in
various exceptional cases as documented in :ref:`hybrid_chapter`.
.. index::
single: route path pattern syntax
.. _route_pattern_syntax:
Route Pattern Syntax
~~~~~~~~~~~~~~~~~~~~
The syntax of the pattern matching language used by :mod:`pyramid`
URL dispatch in the *pattern* argument is straightforward; it is close
to that of the :term:`Routes` system used by :term:`Pylons`.
The *pattern* used in route configuration may start with a slash
character. If the pattern does not start with a slash character, an
implicit slash will be prepended to it at matching time. For example,
the following patterns are equivalent:
.. code-block:: text
:foo/bar/baz
and:
.. code-block:: text
/:foo/bar/baz
A patttern segment (an individual item between ``/`` characters in the
pattern) may either be a literal string (e.g. ``foo``) *or* it may be
a segment replacement marker (e.g. ``:foo``) or a certain combination
of both.
A segment replacement marker is in the format ``:name``, where this
means "accept any characters up to the next nonalphaunumeric character
and use this as the ``name`` matchdict value." For example, the
following pattern defines one literal segment ("foo") and two dynamic
segments ("baz", and "bar"):
.. code-block:: text
foo/:baz/:bar
The above pattern will match these URLs, generating the following
matchdicts:
.. code-block:: text
foo/1/2 -> {'baz':u'1', 'bar':u'2'}
foo/abc/def -> {'baz':u'abc', 'bar':u'def'}
It will not match the following patterns however:
.. code-block:: text
foo/1/2/ -> No match (trailing slash)
bar/abc/def -> First segment literal mismatch
The match for a segment replacement marker in a segment will be done
only up to the first non-alphanumeric character in the segment in the
pattern. So, for instance, if this route pattern was used:
.. code-block:: text
foo/:name.html
The literal path ``/foo/biz.html`` will match the above route pattern,
and the match result will be ``{'name':u'biz'}``. However, the
literal path ``/foo/biz`` will not match, because it does not contain
a literal ``.html`` at the end of the segment represented by
``:name.html`` (it only contains ``biz``, not ``biz.html``).
This does not mean, however, that you can use two segment replacement
markers in the same segment. For instance, ``/:foo:bar`` is a
nonsensical route pattern. It will never match anything.
Segments must contain at least one character in order to match a
segment replacement marker. For example, for the URL ``/abc/``:
- ``/abc/:foo`` will not match.
- ``/:foo/`` will match.
.. warning:: The must-be-one-character-to-match-segment-marker rule is
new in version 1.3. It is untrue for older releases, but this was
due to a bug.
Note that values representing path segments matched with a
``:segment`` match will be url-unquoted and decoded from UTF-8 into
Unicode within the matchdict. So for instance, the following
pattern:
.. code-block:: text
foo/:bar
When matching the following URL:
.. code-block:: text
foo/La%20Pe%C3%B1a
The matchdict will look like so (the value is URL-decoded / UTF-8
decoded):
.. code-block:: text
{'bar':u'La Pe\xf1a'}
If the pattern has a ``*`` in it, the name which follows it is
considered a "remainder match". A remainder match *must* come at the
end of the pattern. Unlike segment replacement markers, it does not
need to be preceded by a slash. For example:
.. code-block:: text
foo/:baz/:bar*fizzle
The above pattern will match these URLs, generating the following
matchdicts:
.. code-block:: text
foo/1/2/ -> {'baz':'1', 'bar':'2', 'fizzle':()}
foo/abc/def/a/b/c -> {'baz':'abc', 'bar':'def', 'fizzle':('a', 'b', 'c')}
Note that when a ``*stararg`` remainder match is matched, the value
put into the matchdict is turned into a tuple of path segments
representing the remainder of the path. These path segments are
url-unquoted and decoded from UTF-8 into Unicode. For example, for
the following pattern:
.. code-block:: text
foo/*fizzle
When matching the following path:
.. code-block:: text
/foo/La%20Pe%C3%B1a/a/b/c
Will generate the following matchdict:
.. code-block:: text
{'fizzle':(u'La Pe\xf1a', u'a', u'b', u'c')}
.. index::
single: route ordering
Route Declaration Ordering
~~~~~~~~~~~~~~~~~~~~~~~~~~
Because route configuration declarations are evaluated in a specific
order when a request enters the system, route configuration
declaration ordering is very important.
The order that routes declarations are evaluated is the order in which
they are added to the application at startup time. This is unlike
:term:`traversal`, which depends on emergent behavior which happens as
a result of traversing a graph.
The order that routes are evaluated when they are defined via
:term:`ZCML` is the order in which they appear in the ZCML relative to
each other. For routes added via the
:mod:`pyramid.configuration.Configurator.add_route` method, the
order that routes are evaluated is the order in which they are added
to the configuration imperatively.
For example, route configuration statements with the following
patterns might be added in the following order:
.. code-block:: text
members/:def
members/abc
In such a configuration, the ``members/abc`` pattern would *never* be
matched; this is because the match ordering will always match
``members/:def`` first; the route configuration with ``members/abc``
will never be evaluated.
.. index::
single: route factory
.. _route_factories:
Route Factories
~~~~~~~~~~~~~~~
A "route" configuration declaration can mention a "factory". When
that route matches a request, and a factory is attached to a route,
the :term:`root factory` passed at startup time to the
:term:`Configurator` is ignored; instead the factory associated with
the route is used to generate a :term:`root` object. This object will
usually be used as the :term:`context` of the view callable ultimately
found via :term:`view lookup`.
.. code-block:: xml
In this way, each route can use a different factory, making it
possible to supply a different :term:`context` object to the view
related to each particular route.
Supplying a different context for each route is useful when you're
trying to use a :mod:`pyramid` :term:`authorization policy` to
provide declarative "context-sensitive" security checks; each context
can maintain a separate :term:`ACL`, as in
:ref:`using_security_with_urldispatch`. It is also useful when you
wish to combine URL dispatch with :term:`traversal` as documented
within :ref:`hybrid_chapter`.
Route Configuration Arguments
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Route configuration statements may specify a large number of
arguments.
Many of these arguments are :term:`route predicate` arguments. A
route predicate argument specifies that some aspect of the request
must be true for the associated route to be considered a match during
the route matching process.
Other arguments are view configuration related arguments. These only
have an effect when the route configuration names a ``view``.
Other arguments are ``name`` and ``factory``. These arguments
represent neither predicates nor view configuration information.
**Non-Predicate Arguments**
``name``
The name of the route, e.g. ``myroute``. This attribute is
required. It must be unique among all defined routes in a given
application.
``factory``
A Python object (often a function or a class) or a :term:`dotted
Python name` to such an object that will generate a
:mod:`pyramid` :term:`context` object when this route
matches. For example, ``mypackage.models.MyFactoryClass``. If this
argument is not specified, the traversal root factory will be used.
``traverse``
If you would like to cause the :term:`context` to be something other
than the :term:`root` object when this route matches, you can spell
a traversal pattern as the ``traverse`` argument. This traversal
pattern will be used as the traversal path: traversal will begin at
the root object implied by this route (either the global root, or
the object returned by the ``factory`` associated with this route).
The syntax of the ``traverse`` argument is the same as it is for
``pattern``. For example, if the ``pattern`` provided is
``articles/:article/edit``, and the ``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 ``/1``. This means that the root object's
``__getitem__`` will be called with the name ``1`` during the
traversal phase. If the ``1`` object 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 ``pattern``.
A similar combining of routing and traversal is available when a
route is matched which contains a ``*traverse`` remainder marker in
its pattern (see :ref:`using_traverse_in_a_route_pattern`). The
``traverse`` argument allows you to associate route patterns with an
arbitrary traversal path without using a a ``*traverse`` remainder
marker; instead you can use other match information.
Note that the ``traverse`` argument is ignored when attached to a
route that has a ``*traverse`` remainder marker in its pattern.
**Predicate Arguments**
``pattern``
The path of the route e.g. ``ideas/:idea``. This argument is
required. See :ref:`route_path_pattern_syntax` for information
about the syntax of route paths. If the path doesn't match the
current URL, route matching continues.
.. note:: In earlier releases of this framework, this argument existed
as ``path``. ``path`` continues to work as an alias for
``pattern``.
``xhr``
This value should be either ``True`` or ``False``. If this value is
specified and is ``True``, the :term:`request` must possess an
``HTTP_X_REQUESTED_WITH`` (aka ``X-Requested-With``) header for this
route to match. This is useful for detecting AJAX requests issued
from jQuery, Prototype and other Javascript libraries. If this
predicate returns ``False``, route matching continues.
``request_method``
A string representing an HTTP method name, e.g. ``GET``, ``POST``,
``HEAD``, ``DELETE``, ``PUT``. If this argument is not specified,
this route will match if the request has *any* request method. If
this predicate returns ``False``, route matching continues.
``path_info``
This value represents a regular expression pattern that will be
tested against the ``PATH_INFO`` WSGI environment variable. If the
regex matches, this predicate will return ``True``. If this
predicate returns ``False``, route matching continues.
``request_param``
This value can be any string. A view declaration with this argument
ensures that the associated route will only match when the request
has a key in the ``request.params`` dictionary (an HTTP ``GET`` or
``POST`` variable) that has a name which matches the supplied value.
If the value supplied as the argument has a ``=`` sign in it,
e.g. ``request_params="foo=123"``, then the key (``foo``) must both
exist in the ``request.params`` dictionary, and the value must match
the right hand side of the expression (``123``) for the route to
"match" the current request. If this predicate returns ``False``,
route matching continues.
``header``
This argument represents an HTTP header name or a header name/value
pair. If the argument contains a ``:`` (colon), it will be
considered a name/value pair (e.g. ``User-Agent:Mozilla/.*`` or
``Host:localhost``). If the value contains a colon, the value
portion should be a regular expression. If the value does not
contain a colon, the entire value will be considered to be the
header name (e.g. ``If-Modified-Since``). If the value evaluates to
a header name only without a value, the header specified by the name
must be present in the request for this predicate to be true. If
the value evaluates to a header name/value pair, the header
specified by the name must be present in the request *and* the
regular expression specified as the value must match the header
value. Whether or not the value represents a header name or a
header name/value pair, the case of the header name is not
significant. If this predicate returns ``False``, route matching
continues.
``accept``
This value represents a match query for one or more mimetypes in the
``Accept`` HTTP request header. If this value is specified, it must
be in one of the following forms: a mimetype match token in the form
``text/plain``, a wildcard mimetype match token in the form
``text/*`` or a match-all wildcard mimetype match token in the form
``*/*``. If any of the forms matches the ``Accept`` header of the
request, this predicate will be true. If this predicate returns
``False``, route matching continues.
``custom_predicates``
This value should be a sequence of references to custom predicate
callables. Use custom predicates when no set of predefined
predicates does what you need. Custom predicates can be combined
with predefined predicates as necessary. Each custom predicate
callable should accept two arguments: ``context`` and ``request``
and should return either ``True`` or ``False`` after doing arbitrary
evaluation of the context and/or the request. If all callables
return ``True``, the associated route will be considered viable for
a given request. If any custom predicate returns ``False``, route
matching continues. Note that the value ``context`` will always be
``None`` when passed to a custom route predicate.
**View-Related Arguments**
``view``
A Python object or a :term:`dotted Python name` to such an object
that will be used as a view callable when this route
matches. e.g. ``mypackage.views.my_view``.
``view_context``
A class or an :term:`interface` (or a :term:`dotted Python name` to
such an object) that the :term:`context` of the view should match
for the view named by the route to be used. This argument is only
useful if the ``view`` attribute is used. If this attribute is not
specified, the default (``None``) will be used.
If the ``view`` argument is not provided, this argument has
no effect.
This attribute can also be spelled as ``for_`` or ``view_for``.
``view_permission``
The permission name required to invoke the view associated with this
route. e.g. ``edit``. (see :ref:`using_security_with_urldispatch`
for more information about permissions).
If the ``view`` attribute is not provided, this argument has
no effect.
This argument can also be spelled as ``permission``.
``view_renderer``
This is either a single string term (e.g. ``json``) or a string
implying a path or :term:`resource specification`
(e.g. ``templates/views.pt``). If the renderer value is a single
term (does not contain a dot ``.``), the specified term will be used
to look up a renderer implementation, and that renderer
implementation will be used to construct a response from the view
return value. If the renderer term contains a dot (``.``), the
specified term will be treated as a path, and the filename extension
of the last element in the path will be used to look up the renderer
implementation, which will be passed the full path. The renderer
implementation will be used to construct a response from the view
return value. See :ref:`views_which_use_a_renderer` for more
information.
If the ``view`` argument is not provided, this argument has
no effect.
This argument can also be spelled as ``renderer``.
``view_attr``
The view machinery defaults to using the ``__call__`` method of the
view callable (or the function itself, if the view callable is a
function) to obtain a response dictionary. The ``attr`` value
allows you to vary the method attribute used to obtain the response.
For example, if your view was a class, and the class has a method
named ``index`` and you wanted to use this method instead of the
class' ``__call__`` method to return the response, you'd say
``attr="index"`` in the view configuration for the view. This is
most useful when the view definition is a class.
If the ``view`` argument is not provided, this argument has no
effect.
``use_global_views``
When a request matches this route, and view lookup cannot find a view
which has a 'route_name' predicate argument that matches the route,
try to fall back to using a view that otherwise matches the context,
request, and view name (but does not match the route name predicate).
.. _custom_route_predicates:
Custom Route Predicates
~~~~~~~~~~~~~~~~~~~~~~~
Each of the predicate callables fed to the ``custom_predicates``
argument of :meth:`pyramid.configuration.Configurator.add_route` or
the ``custom_predicates`` ZCML attribute must be a callable accepting
two arguments. The first argument passed to a custom predicate is a
dictionary conventionally named ``info``. The second argument is the
current :term:`request` object.
The ``info`` dictionary has a number of contained values: ``match`` is
a dictionary: it represents the arguments matched in the URL by the
route. ``route`` is an object representing the route which was
matched (see :class:`pyramid.interfaces.IRoute` for the API of such
a route object).
``info['match']`` is useful when predicates need access to the route
match. For example:
.. code-block:: python
def any_of(segment_name, *allowed):
def predicate(info, request):
if info['match'][segment_name] in allowed:
return True
return predicate
num_one_two_or_three = any_of('num, 'one', 'two', 'three')
config.add_route('num', '/:num',
custom_predicates=(num_one_two_or_three,))
The above ``any_of`` function generates a predicate which ensures that
the match value named ``segment_name`` is in the set of allowable
values represented by ``allowed``. We use this ``any_of`` function to
generate a predicate function named ``num_one_two_or_three``, which
ensures that the ``num`` segment is one of the values ``one``,
``two``, or ``three`` , and use the result as a custom predicate by
feeding it inside a tuple to the ``custom_predicates`` argument to
:meth:`repoze.configuration.Configurator.add_route`.
A custom route predicate may also *modify* the ``match`` dictionary.
For instance, a predicate might do some type conversion of values:
.. code-block:: python
def integers(*segment_names):
def predicate(info, request):
match = info['match']
for segment_name in segment_names:
try:
match[segment_name] = int(match[segment_name])
except (TypeError, ValueError):
pass
return True
return predicate
ymd_to_int = integers('year', 'month', 'day')
config.add_route('num', '/:year/:month/:day',
custom_predicates=(ymd_to_int,))
Note that a conversion predicate is still a predicate so it must
return ``True`` or ``False``; a predicate that does *only* conversion,
such as the one we demonstrate above should unconditionally return
``True``.
The ``match`` dictionary passed within ``info`` to each predicate
attached to a route will be the same dictionary. Therefore, when
registering a custom predicate which modifies the ``match`` dict, the
code registering the predicate should usually arrange for the
predicate to be the *last* custom predicate in the custom predicate
list. Otherwise, custom predicates which fire subsequent to the
predicate which performs the ``match`` modification will receive the
*modified* match dictionary.
.. warning::
It is a poor idea to rely on ordering of custom predicates to build
some conversion pipeline, where one predicate depends on the side
effect of another. For instance, it's a poor idea to register two
custom predicates, one which handles conversion of a value to an
int, the next which handles conversion of that integer to some
custom object. Just do all that in a single custom predicate.
The ``route`` object in the ``info`` dict is an object that has two
useful attributes: ``name`` and ``pattern``. The ``name`` attribute
is the route name. The ``pattern`` attribute is the route pattern.
An example of using the route in a set of route predicates:
.. code-block:: python
:linenos:
def twenty_ten(info, request):
if info['route'].name in ('ymd', 'ym', 'y'):
return info['match']['year'] == '2010'
config.add_route('y', '/:year', custom_predicates=(twenty_ten,))
config.add_route('ym', '/:year/:month', custom_predicates=(twenty_ten,))
config.add_route('ymd', '/:year/:month:/day',
custom_predicates=(twenty_ten,))
The above predicate, when added to a number of route configurations
ensures that the year match argument is '2010' if and only if the
route name is 'ymd', 'ym', or 'y'.
See also :class:`pyramid.interfaces.IRoute` for more API
documentation about a route object.
Route Matching
--------------
The main purpose of route configuration is to match (or not match)
the ``PATH_INFO`` present in the WSGI environment provided during a
request against a URL path pattern.
The way that :mod:`pyramid` does this is very simple. When a
request enters the system, for each route configuration declaration
present in the system, :mod:`pyramid` checks the ``PATH_INFO``
against the pattern declared.
If any route matches, the route matching process stops. The
:term:`request` is decorated with a special :term:`interface` which
describes it as a "route request", the :term:`context` and :term:`view
name` are generated, and the context, the view name, and the resulting
request are handed off to :term:`view lookup`. This process is
otherwise known as :term:`context finding`. During view lookup, if
any ``view`` argument was provided within the matched route
configuration, the :term:`view callable` it points to is called.
When a route configuration is declared, it may contain :term:`route
predicate` arguments. All route predicates associated with a route
declaration must be ``True`` for the route configuration to be used
for a given request.
If any predicate in the set of :term:`route predicate` arguments
provided to a route configuration returns ``False``, that route is
skipped and route matching continues through the ordered set of
routes.
If no route matches after all route patterns are exhausted,
:mod:`pyramid` falls back to :term:`traversal` to do :term:`context
finding` and :term:`view lookup`.
.. index::
single: matchdict
The Matchdict
~~~~~~~~~~~~~
When the URL pattern associated with a particular route configuration
is matched by a request, a dictionary named ``matchdict`` is added as
an attribute of the :term:`request` object. Thus,
``request.matchdict`` will contain the values that match replacement
patterns in the ``pattern`` element. The keys in a matchdict will be
strings. The values will be Unicode objects.
.. note::
If no route URL pattern matches, no ``matchdict`` is attached to
the request.
.. index::
single: matched_route
The Matched Route
~~~~~~~~~~~~~~~~~
When the URL pattern associated with a particular route configuration
is matched by a request, an object named ``matched_route`` is added as
an attribute of the :term:`request` object. Thus,
``request.matched_route`` will be an object implementing the
:class:`pyramid.interfaces.IRoute` interface which matched the
request. The most useful attribute of the route object is ``name``,
which is the name of the route that matched.
Routing Examples
----------------
Let's check out some examples of how route configuration statements
might be commonly declared, and what will happen if they are matched
by the information present in a request. The examples that follow
assume that :term:`ZCML` will be used to perform route configuration,
although you can use :term:`imperative configuration` equivalently if
you like.
.. _urldispatch_example1:
Example 1
~~~~~~~~~
The simplest route declaration which configures a route match to
*directly* result in a particular view callable being invoked:
.. code-block:: xml
:linenos:
When a route configuration with a ``view`` attribute is added to the
system, and an incoming request matches the *pattern* of the route
configuration, the :term:`view callable` named as the ``view``
attribute of the route configuration will be invoked.
In the case of the above example, when the URL of a request matches
``/site/:id``, the view callable at the Python dotted path name
``mypackage.views.site_view`` will be called with the request. In
other words, we've associated a view callable directly with a route
pattern.
When the ``/site/:id`` route pattern matches during a request, the
``site_view`` view callable is invoked with that request as its sole
argument. When this route matches, a ``matchdict`` will be generated
and attached to the request as ``request.matchdict``. If the specific
URL matched is ``/site/1``, the ``matchdict`` will be a dictionary
with a single key, ``id``; the value will be the string ``'1'``, ex.:
``{'id':'1'}``.
The ``mypackage.views`` module referred to above might look like so:
.. code-block:: python
:linenos:
from webob import Response
def site_view(request):
return Response(request.matchdict['id'])
The view has access to the matchdict directly via the request, and can
access variables within it that match keys present as a result of the
route pattern.
See :ref:`views_chapter` for more information about views.
Example 2
~~~~~~~~~
Below is an example of a more complicated set of route statements you
might add to your application:
.. code-block:: xml
:linenos:
The above configuration will allow :mod:`pyramid` to service URLs
in these forms:
.. code-block:: text
/ideas/:idea
/users/:user
/tags/:tag
- When a URL matches the pattern ``/ideas/:idea``, the view available
at the dotted Python pathname ``mypackage.views.idea_view`` will be
called. For the specific URL ``/ideas/1``, the ``matchdict``
generated and attached to the :term:`request` will consist of
``{'idea':'1'}``.
- When a URL matches the pattern ``/users/:user``, the view available
at the dotted Python pathname ``mypackage.views.user_view`` will be
called. For the specific URL ``/users/1``, the ``matchdict``
generated and attached to the :term:`request` will consist of
``{'user':'1'}``.
- When a URL matches the pattern ``/tags/:tag``, the view available
at the dotted Python pathname ``mypackage.views.tag_view`` will be
called. For the specific URL ``/tags/1``, the ``matchdict``
generated and attached to the :term:`request` will consist of
``{'tag':'1'}``.
In this example we've again associated each of our routes with a
:term:`view callable` directly. In all cases, the request, which will
have a ``matchdict`` attribute detailing the information found in the
URL by the process will be passed to the view callable.
Example 3
~~~~~~~~~
The context object passed in to a view found as the result of URL
dispatch will, by default, be an instance of the object returned by
the :term:`root factory` configured at startup time (the
``root_factory`` argument to the :term:`Configurator` used to
configure the application).
You can override this behavior by passing in a ``factory`` argument to
the ZCML directive for a particular route. The ``factory`` should be
a callable that accepts a :term:`request` and returns an instance of a
class that will be the context used by the view.
An example of using a route with a factory:
.. code-block:: xml
:linenos:
The above route will manufacture an ``Idea`` model as a
:term:`context`, assuming that ``mypackage.models.Idea`` resolves to a
class that accepts a request in its ``__init__``. For example:
.. code-block:: python
:linenos:
class Idea(object):
def __init__(self, request):
pass
In a more complicated application, this root factory might be a class
representing a :term:`SQLAlchemy` model.
Example 4
~~~~~~~~~
It is possible to create a route declaration without a ``view``
attribute, but associate the route with a :term:`view callable` using
a ``view`` declaration.
.. code-block:: xml
:linenos:
This set of configuration parameters creates a configuration
completely equivalent to this example provided in
:ref:`urldispatch_example1`:
.. code-block:: xml
:linenos:
In fact, the spelling which names a ``view`` attribute is just
syntactic sugar for the more verbose spelling which contains separate
view and route registrations.
More uses for this style of associating views with routes are explored
in :ref:`hybrid_chapter`.
.. index::
single: matching the root URL
single: root url (matching)
Matching the Root URL
---------------------
It's not entirely obvious how to use a route pattern to match the root
URL ("/"). To do so, give the empty string as a pattern in a ZCML
``route`` declaration:
.. code-block:: xml
:linenos:
Or provide the literal string ``/`` as the pattern:
.. code-block:: xml
:linenos:
.. index::
single: generating route URLs
single: route URLs
Generating Route URLs
---------------------
Use the :func:`pyramid.url.route_url` function to generate URLs
based on route patterns. For example, if you've configured a route in
ZCML with the ``name`` "foo" and the ``pattern`` ":a/:b/:c", you might
do this.
.. ignore-next-block
.. code-block:: python
:linenos:
from pyramid.url import route_url
url = route_url('foo', request, a='1', b='2', c='3')
This would return something like the string
``http://example.com/1/2/3`` (at least if the current protocol and
hostname implied ``http:/example.com``). See the
:func:`pyramid.url.route_url` API documentation for more
information.
.. index::
single: redirecting to slash-appended routes
.. _redirecting_to_slash_appended_routes:
Redirecting to Slash-Appended Routes
------------------------------------
For behavior like Django's ``APPEND_SLASH=True``, use the
:func:`pyramid.view.append_slash_notfound_view` view as the
:term:`Not Found view` in your application. When this view is the Not
Found view (indicating that no view was found), and any routes have
been defined in the configuration of your application, if the value of
``PATH_INFO`` does not already end in a slash, and if the value of
``PATH_INFO`` *plus* a slash matches any route's pattern, it does an
HTTP redirect to the slash-appended ``PATH_INFO``.
Let's use an example, because this behavior is a bit magical. If the
``append_slash_notfound_view`` is configured in your application and
your route configuration looks like so:
.. code-block:: xml
:linenos:
If a request enters the application with the ``PATH_INFO`` value of
``/no_slash``, the first route will match. If a request enters the
application with the ``PATH_INFO`` value of ``/no_slash/``, *no* route
will match, and the slash-appending "not found" view will *not* find a
matching route with an appended slash.
However, if a request enters the application with the ``PATH_INFO``
value of ``/has_slash/``, the second route will match. If a request
enters the application with the ``PATH_INFO`` value of ``/has_slash``,
a route *will* be found by the slash appending notfound view. An HTTP
redirect to ``/has_slash/`` will be returned to the user's browser.
Note that this will *lose* ``POST`` data information (turning it into
a GET), so you shouldn't rely on this to redirect POST requests.
To configure the slash-appending not found view in your application,
change the application's ``configure.zcml``, adding the following
stanza:
.. code-block:: xml
:linenos:
Or use the :meth:`pyramid.configuration.Configurator.add_view`
method if you don't use ZCML:
.. code-block:: python
:linenos:
from pyramid.exceptions import NotFound
from pyramid.view import append_slash_notfound_view
config.add_view(append_slash_notfound_view, context=NotFound)
See :ref:`view_module` and :ref:`changing_the_notfound_view` for more
information about the slash-appending not found view and for a more
general description of how to configure a not found view.
Custom Not Found View With Slash Appended Routes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
There can only be one :term:`Not Found view` in any :mod:`pyramid`
application. Even if you use
:func:`pyramid.view.append_slash_notfound_view` as the Not Found
view, :mod:`pyramid` still must generate a ``404 Not Found``
response when it cannot redirect to a slash-appended URL; this not
found response will be visible to site users.
If you don't care what this 404 response looks like, and only you need
redirections to slash-appended route URLs, you may use the
:func:`pyramid.view.append_slash_notfound_view` object as the Not
Found view as described above. However, if you wish to use a *custom*
notfound view callable when a URL cannot be redirected to a
slash-appended URL, you may wish to use an instance of the
:class:`pyramid.view.AppendSlashNotFoundViewFactory` class as the
Not Found view, supplying a :term:`view callable` to be used as the
custom notfound view as the first argument to its constructor. For
instance:
.. code-block:: python
from pyramid.exceptions import NotFound
from pyramid.view import AppendSlashNotFoundViewFactory
def notfound_view(context, request):
return HTTPNotFound('It aint there, stop trying!')
custom_append_slash = AppendSlashNotFoundViewFactory(notfound_view)
config.add_view(custom_append_slash, context=NotFound)
The ``notfound_view`` supplied must adhere to the two-argument view
callable calling convention of ``(context, request)`` (``context``
will be the exception object).
.. _cleaning_up_after_a_request:
Cleaning Up After a Request
---------------------------
Sometimes it's required that some cleanup be performed at the end of a
request when a database connection is involved. When
:term:`traversal` is used, this cleanup is often done as a side effect
of the traversal :term:`root factory`. Often the root factory will
insert an object into the WSGI environment that performs some cleanup
when its ``__del__`` method is called. When URL dispatch is used,
however, no special root factory is required, so sometimes that option
is not open to you.
Instead of putting this cleanup logic in the root factory, however,
you can cause a subscriber to be fired when a new request is detected;
the subscriber can do this work.
For example, let's say you have a ``mypackage`` :mod:`pyramid`
application package that uses SQLAlchemy, and you'd like the current
SQLAlchemy database session to be removed after each request. Put the
following in the ``mypackage.run`` module:
.. ignore-next-block
.. code-block:: python
:linenos:
from mypackage.sql import DBSession
class Cleanup:
def __init__(self, cleaner):
self.cleaner = cleaner
def __del__(self):
self.cleaner()
def handle_teardown(event):
environ = event.request.environ
environ['mypackage.sqlcleaner'] = Cleanup(DBSession.remove)
Then in the ``configure.zcml`` of your package, inject the following:
.. code-block:: xml
Or, if you don't use ZCML, but you do use a :term:`scan` add a
subscriber decorator:
.. code-block:: python
from pyramid.events import subscriber
from pyramid.interfaces import INewRequest
@subscriber(INewRequest)
def handle_teardown(event):
environ = event.request.environ
environ['mypackage.sqlcleaner'] = Cleanup(DBSession.remove)
Or finally, it can be done imperatively via the ``add_subscriber``
method of a :term:`Configurator`.
.. code-block:: python
from pyramid.interfaces import INewRequest
from pyramid.configuration imoport Configurator
def handle_teardown(event):
environ = event.request.environ
environ['mypackage.sqlcleaner'] = Cleanup(DBSession.remove)
config = Configurator()
config.add_subscriber(handle_teardown, INewRequest)
Any of the above three ways to register a handle_teardown subscriber
will cause the DBSession to be removed whenever the WSGI environment
is destroyed (usually at the end of every request).
.. note:: This is only an example. In particular, it is not necessary
to cause ``DBSession.remove`` to be called as the result of an
event listener in an application generated from any
:mod:`pyramid` paster template, because these all use the
``repoze.tm2`` middleware. The cleanup done by
``DBSession.remove`` is unnecessary when ``repoze.tm2`` middleware
is in the WSGI pipeline.
.. index::
pair: URL dispatch; security
.. _using_security_with_urldispatch:
Using :mod:`pyramid` Security With URL Dispatch
--------------------------------------------------
:mod:`pyramid` provides its own security framework which consults an
:term:`authorization policy` before allowing any application code to
be called. This framework operates in terms of an access control
list, which is stored as an ``__acl__`` attribute of a context object.
A common thing to want to do is to attach an ``__acl__`` to the
context object dynamically for declarative security purposes. You can
use the ``factory`` argument that points at a factory which attaches a
custom ``__acl__`` to an object at its creation time.
Such a ``factory`` might look like so:
.. code-block:: python
:linenos:
class Article(object):
def __init__(self, request):
matchdict = request.matchdict
article = matchdict.get('article', None)
if article == '1':
self.__acl__ = [ (Allow, 'editor', 'view') ]
If the route ``archives/:article`` is matched, and the article number
is ``1``, :mod:`pyramid` will generate an ``Article``
:term:`context` with an ACL on it that allows the ``editor`` principal
the ``view`` permission. Obviously you can do more generic things
than inspect the routes match dict to see if the ``article`` argument
matches a particular string; our sample ``Article`` factory class is
not very ambitious.
.. note:: See :ref:`security_chapter` for more information about
:mod:`pyramid` security and ACLs.
References
----------
A tutorial showing how :term:`URL dispatch` can be used to create a
:mod:`pyramid` application exists in :ref:`bfg_sql_wiki_tutorial`.