==============
Routing System
==============

When it comes to combining multiple controller or view functions (however
you want to call them) you need a dispatcher. A simple way would be
applying regular expression tests on the ``PATH_INFO`` and call registered
callback functions that return the value then.

Werkzeug provides a much more powerful system, similar to `routes`_.

Quickstart
==========

Here a simple example which could be the URL definition for a blog:

.. sourcecode:: python

    from werkzeug.routing import Map, Rule, NotFound, RequestRedirect

    map = Map([
        Rule('/', endpoint='blog/index'),
        Rule('/<int:year>/', endpoint='blog/archive'),
        Rule('/<int:year>/<int:month>/', endpoint='blog/archive'),
        Rule('/<int:year>/<int:month>/<int:day>/', endpoint='blog/archive'),
        Rule('/<int:year>/<int:month>/<int:day>/<slug>', endpoint='blog/show_post'),
        Rule('/about', endpoint='blog/about_me'),
        Rule('/feeds/', endpoint='blog/feeds'),
        Rule('/feeds/<feed_name>.rss', endpoint='blog/show_feed')
    ])

    def application(environ, start_response):
        urls = map.bind_to_environ(environ)
        try:
            endpoint, args = urls.match(environ.get('PATH_INFO') or '/')
        except NotFound:
            response = 'Rule Not Found'
        except RequestRedirect, e:
            response = 'Rule redirects to %s' % e.new_url
        else:
            response = 'Rule points to %r with arguments %r' % (endpoint, args)
        start_response('200 OK', [('Content-Type', 'text/plain')])
        yield response

So what does that do? First of all we create a new `Map` which stores a
bunch of URL rules. Then we pass it a list of `Rule` objects.

Each `Rule` object is instantiated with a string that represents a rule and
an endpoint which will be the alias for what view the rule represents.
Multiple rules can have the same endpoint but should have different
arguments to allow URL construction.

The format for the URL rules is straightforward but explained in detail below.

Inside the WSGI application we bind the map to the current request which will
return a new `MapAdapter`. This map adapter can then be used to match or
build domains for the current request.

The `match` method can then either return a tuple in the form
``(endpoint, args)`` or raise one of the two exceptions `NotFound` or
`RequestRedirect`. The first one is raised if there is no URL matching the
current request, the latter if a rule was found that redirects to another
rule. This for example happens if a user visits ``/feeds`` but only ``/feeds/``
is matching.

The redirect test can be disabled but it's a good idea to keep it activated
because it helps you create unique URLs.

Rule Format
===========

Rule strings basically are just normal url paths with placeholders in the
format ``<converter(arguments):name>`` where converter and the arguments
are optional. If no converter is defined the `default` converter is used
which means `string` in the normal configuration.

URL rules that end with a slash are branch URLs, others are leaves. If you
have `strict_slashes` enabled which is the default, all branch URLs that
are visited without a trailing slash will trigger a redirect to the same
URL with that slash appended.

The list of converters can be extended, the default converters are explained
below.

Builtin Converters
==================

Here a list of converters that come with Werkzeug:

`string`
    This converter is the default converter and accepts any string but
    only one one path segment. Thus the string can not include a slash.

    Supported arguments:

    - `minlength` - the minimum length of the string. must be greater
      than 1.
    - `maxlength` - the maximum length of the string.
    - `length` - the exact length of that string.


`path`
    Matches everything including a slash. No further configuration.


`int`
    This converter only accepts integer values.

    Supported arguments:

    - `fixed_digits` - the number of fixed digits in the URL. If you
      set this to ``4`` for example, the application will only match
      if the url looks like ``/0001/``. The default is
      variable length.
    - `min` - the minimal value.
    - `max` - the maximal value.

`float`
    Like `int` but it returns and takes a floating point number.

.. admonition:: Important

    Werkzeug evaluates converter arguments as if they are python method
    calls. Thus you should **never** create rules from user submitted
    data since they could insert arbitrary python code in the parameters
    part.

    The advantage of this method is that you can access variables defined
    outside of the url definition:

    .. sourcecode:: python

        PICTURE_ID_LENGTH = 4

        map = Map([
            Rule('/picture/<int(fixed_digits=PICTURE_ID_LENGTH):id>.png',
                 endpoint='view_image')
        ])

Map Options
===========

Some of the configuration values are only stored on the `Map` instance since
those affect all rules. Other are just defaults and can be overridden for
each rule. Note that you have to specify all arguments beside the `rules`
as keywords arguments!

`rules`
    First argument; can be positional argument. A sequence of rules or
    rule factories for this map.

`default_subdomain`
    The default subdomain. If no subdomain is defined this is used. The
    default value is no subdomain. This only takes effect if subdomains
    are in use.

`charset`
    The charset for all URLs. Defaults to `ascii`.

`strict_slashes`
    If this is enable werkzeug will take care of trailing slashes, otherwise
    those are stripped before matching. Default is enabled.

`redirect_defaults`
    If there are defaults (explained below) and you don't want redirecting
    you can disable this rule.

`converters`
    A dict of additional converters.

Rule Options
============

There are some options for `Rule` that change the way it behaves. Note that
beside the rule string itself all arguments *must* be keyword arguments in
order to not break the application on Werkzeug upgrades.

`string`
    The first argument which is the rule format as explained above.

`endpoint`
    The endpoint for this rule. This can be anything. A reference to a
    function, a string, a number etc. The preferred way is using a string
    as endpoint because you'll use the endpoint for url generation.

`defaults`
    An optional dict with defaults for other rules with the same endpoint.
    This is a bit tricky but useful if you want to have unique URLs:

    .. sourcecode:: python

        map = Map([
            Rule('/all/', defaults={'page': 1}, endpoint='all_entries'),
            Rule('/all/<int:page_number>', endpoint='all_entries')
        ])

    If a user now visits ``http://example.com/all/1`` he will be redirected
    to ``http://example.com/all/``. If `redirect_defaults` is disabled on
    the `Map` instance this will only affect the URL generation.

`subdomain`
    The subdomain rule string for this rule. If not specified the rule only
    matches for the `default_subdomain` of the map. If the map is not bound
    to a subdomain this feature is disabled.

    Can be useful if you want to have user profiles on different subdomains
    and all subdomains are forwarded to your application:

    .. sourcecode:: python

        map = Map([
            Rule('/', subdomain='<username>', endpoint='user/homepage'),
            Rule('/stats', subdomain='<username>', endpoint='user/stats')
        ])

`methods`
    A sequence of http methods this rule applies to. If not specified, all
    methods are allowed. For example this can be useful if you want different
    endpoints for `POST` and `GET`.

`strict_slashes`
    Override the `Map` setting for `strict_slashes` only for this rule. If
    not specified the `Map` setting is used.

`build_only`
    Set this to true and the rule will never match but will create a URL
    that can be build. This is useful if you have resources on a subdomain
    or folder that are not handled by the WSGI application (like static data)

Rule Factories
==============

As soon as you have more complex URL setups it's a good idea to use the
rule factories provided. They automate some of the repetitive tasks. They
also allow you to span rules over multiple python modules and combining
them to one map.

`Subdomain`
    All URLs provided by this factory have the subdomain set to a
    specific domain. For example if you want to use the subdomain for
    the current language this can be a good setup:

    .. sourcecode:: python

        from werkzeug.routing import Map, Rule, Subdomain

        map = Map([
            Rule('/', endpoint='#select_language'),
            Subdomain('<string(length=2):lang_code>', [
                Rule('/', endpoint='index'),
                Rule('/about', endpoint='about'),
                Rule('/help', endpoint='help')
            ])
        ])

    All the rules except of the ``'#select_language'`` endpoint will now
    listen on a two letter long subdomain that helds the language code
    for the current request.

`Submount`
    Like `Subdomain` but prefixes the URL rule with a given string:

    .. sourcecode:: python

        from werkzeug.routing import Map, Rule, Submount

        map = Map([
            Rule('/', endpoint='index'),
            Submount('/blog', [
                Rule('/', endpoint='blog/index'),
                Rule('/entry/<entry_slug>', endpoint='blog/show')
            ])
        ])

    Now the rule ``'blog/show'`` listens on ``/blog/entry/<entry_slug>`` etc.

`EndpointPrefix`
    Prefixes all endpoints (which must be strings for this factory) with
    another string. This can be useful for the blog example above:

    .. sourcecode:: python

        from werkzeug.routing import Map, Rule, Submount, EndpointPrefix

        map = Map([
            Rule('/', endpoint='index'),
            EndpointPrefix('blog/', [Submount('/blog', [
                Rule('/', endpoint='index'),
                Rule('/entry/<entry_slug>', endpoint='show')
            ])])
        ])

Matching and Building
=====================

Once the URLs are set up you usually want to connect your map with the views
or controllers or something similar. There are also ways to generate URLs
by their endpoint and provided parameters.

Binding a Map to a Request
--------------------------

Because Werkzeug routing is subdomain and aware you have to bind the map to
maybe changing request variables. This process is called `binding` and returns
a new object which is called an `MapAdapter` that knows how to match and build
URLs for the current request.

There are basically two ways to bind a request:

`bind(server_name, script_name=None, subdomain=None, url_scheme='http')`
    Return a new `MapAdapter` with the details specified to the call. Note
    that `script_name` will default to ``'/'`` if not further specified
    or `None`. The `server_name` at least is a requirement because the HTTP
    RFC requires absolute URLs for redirects and so all redirect exceptions
    raised by Werkzeug will contain the full canonical URL.

    `subdomain` will default to the `default_subdomain` for this map if not
    defined. If there is no `default_subdomain` you cannot use the subdomain
    feature.

`bind_to_environ(environ, server_name=None, subdomain=None)`
    Like `bind` but you can pass it an WSGI environment and it will fetch
    the information from that directory. Note that because of limitations in
    the protocol there is no way to get the current subdomain and real
    `server_name` from the environment. If you don't provide it, Werkzeug
    will use `SERVER_NAME` and `SERVER_PORT` (or `HTTP_HOST` if provided)
    as used `server_name` with disabled subdomain feature.

    If `subdomain` is `None` but an environment and a server name is
    provided it will calculate the current subdomain automatically.
    Example: `server_name` is ``'example.com'`` and the `SERVER_NAME`
    in the wsgi `environ` is ``'staging.dev.example.com'`` the calculated
    subdomain will be ``'staging.dev'``.


Matching URLs
-------------

Once you have a `MapAdapter` object you can use the `match` method provided
to match a given `PATH_INFO` against the map adapter. The usage is simple:
you just pass the `adapter.match` method the current path info as well as
the method (which defaults to `GET`). The following things can then happen:

- you receive a `NotFound` exception that indicates that no URL is matching.

- you receive a `RequestRedirect` exception with a `new_url` attribute. This
  exception is used to notify you about a request Werkzeug requests by your
  WSGI application. This is for example the case if you request ``/foo``
  although the correct URL is ``/foo/``.

- you get a tuple in the form ``(endpoint, arguments)`` when there is a
  match.

Here a small example for matching:

.. sourcecode:: pycon

    >>> from werkzeug.routing import Map, Rule
    >>> m = Map([
    ...     Rule('/', endpoint='index'),
    ...     Rule('/downloads/', endpoint='downloads/index'), 
    ...     Rule('/downloads/<int:download_id>', endpoint='downloads/show')
    ... ])
    >>> urls = m.bind("example.com", "/")
    >>> urls.match("/", "GET")
    ('index', {})
    >>> urls.match("/downloads/42")
    ('downloads/show', {'download_id': 42})

And here what happens on redirect and missing URLs:

.. sourcecode:: pycon

    >>> urls.match("/downloads")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "werkzeug/routing.py", line 660, in match
        path_info.lstrip('/')
    werkzeug.routing.RequestRedirect: http://example.com/downloads/
    >>> urls.match("/missing")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "werkzeug/routing.py", line 676, in match
        raise NotFound(path_info)
    werkzeug.routing.NotFound: /missing

Building URLs
-------------

Building URLs works pretty much the other way round. Instead of `match`
you call `build` and pass it the endpoint and a dict of arguments for the
placeholders.

The `build` function also accepts a third argument (`force_external`)
which, if you set it to `True` will force external URLs. Per default external
URLs (include the server name) will only be used if the target URL is on a
different subdomain.

With the same map as in the example above this code generates some target
URLs:

.. sourcecode:: pycon

    >>> urls.build("index", {})
    '/'
    >>> urls.build("downloads/show", {'download_id': 42})
    '/downloads/42'
    >>> urls.build("downloads/show", {'download_id': 42}, True)
    'http://example.com/downloads/42'

Because URLs cannot contain non ASCII data you will always get bytestrings
back. Non ASCII characters are urlencoded with the charset defined on the
map instance.

Additional values are converted to unicode and appended to the URL as
URL querystring parameters:

.. sourcecode:: pycon

    >>> urls.build("index", {'q': 'My Searchstring'})
    '/?q=My+Searchstring'

Custom Converters
=================

You can easily add custom converters. The only thing you have to do is to
subclass `BaseConverter` and pass that new converter to the map. A converter
has to provide two public methods: `to_python` and `to_url` and a member that
represents a regular expression. Here is a small example:

.. sourcecode:: python

    from random import randrange
    from werkzeug.routing import Rule, Map, BaseConverter, ValidationError

    class BooleanConverter(BaseConverter):

        def __init__(self, map, randomify=False):
            super(BooleanConverter, self).__init__(map)
            self.randomify = randomify
            self.regex = '(?:yes|no|maybe)'

        def to_python(self, value):
            if value == 'maybe':
                if self.randomify:
                    return not randrange(0, 1)
                raise ValidationError()
            return value == 'yes'

        def to_value(self, value):
            return value and 'yes' or 'no'

    map = Map([
        Rule('/vote/<bool:werkzeug_rocks>', endpoint='vote'),
        Rule('/vote/<bool(randomify=True):foo>', endpoint='foo')
    ], converters={'bool': BooleanConverter})

If you want that converter to be the default converter name it ``'default'``.


.. _routes: http://routes.groovie.org/
