CherryPy Project Download

Routes URL generation

For those who use Routes and RoutesDispatcher?, making use of the url generation capabilities can be a great timesaver.

To make Routes generate a url, call routes.url_for with keyword parameters corresponding to the Routes rules. To account for urls hidden behind mod_proxy or mod_rewrite it's also good to pass that url through cherrypy.url.

Say we have this setup:

# -*- encoding: UTF-8 -*-
import os

import cherrypy

class MainController:
    def index(self):
        return "This is the main page"

class BlogController:

    def index(self, entry_id):
        return "This is blog entry no. %d" % int(entry_id)

    def edit(self, entry_id):
        return "This is an edit form for blog entry no. %d" % int(entry_id)

dispatcher = None

def setup_routes():
    d = cherrypy.dispatch.RoutesDispatcher()
    d.connect('blog', 'myblog/:entry_id/:action', controller=BlogController())
    d.connect('main', ':action', controller=MainController())
    dispatcher = d
    return dispatcher

conf = {
    '/': {
        'request.dispatch': setup_routes()
    '/static': {
        'tools.staticdir.on': True,
        'tools.staticdir.root': os.path.dirname(os.path.abspath(__file__)),
        'tools.staticdir.dir': 'static'

def start(config):
    cherrypy.quickstart(None, config=conf)

if __name__ == '__main__':
    start(os.path.join(os.path.dirname(__file__), 'app.conf'))

To make use of Routes url-generation, we can for example call cherrypy.url(routes.url_for(controller="blog", action="edit", entry_id="123")) - which generates an url that goes straight to the edit form of blog entry 123. It might be something like

Calling cherrypy.url(routes.url_for(...)) is tedious, especially for a template designer, so let's make things easier:

My goal here is to provide one function - url - which transparently handles both the role of cherrypy.url and routes.url_for. In it's simplest form it looks like this:

import routes
import cherrypy

def url(*args, **kwargs):
    if len(args) == 1 and len(kwargs) == 0 and type(args[0]) in (str, unicode):
        return cherrypy.url(args[0])
        return cherrypy.url(routes.url_for(*args, **kwargs))

This makes it possible to do things like this

>>> url('/static/css/main.css')
>>> url(controller="main")
>>> url(controller="blog", action="edit", entry_id=123)

I find the most common use is the last one, specify a controller, an action and some parameters. In most cases, a controller is a class instance and the action is a method. This makes me want to write something like this:


Of course, this is easy in Python, with the following definition of url

import routes
import cherrypy

class _ctrlchain(object):
    def __init__(self, name, head=None):
        if head is None:
            self.chain = list()
            self.chain = head[:]
    def __getattr__(self, attr):
        return _ctrlchain(attr, self.chain)
    def __call__(self, *args, **kwargs):
        if len(self.chain) > 3:
            raise Exception("Don't know what to do with over 3 chain elements")
        if len(self.chain) > 2:
            kwargs["action"] = self.chain[2]
        if len(self.chain) > 1:
            kwargs["controller"] = self.chain[1]
        if len(args) == 1 and len(kwargs) == 0 and type(args[0]) in (str, unicode):
            return cherrypy.url(args[0])
            return cherrypy.url(routes.url_for(*args, **kwargs))
url = _ctrlchain('urlgen')

This correctly handles all the use cases above. For good measure, here are some more:

>>> url.main()

Of course, this becomes most useful when this function is made available in whatever template language you are using:

<li py:for="entry in">
    <a href="${}" py:content="entry.title">Placeholder for entry title</a>

For more information on Routes url generation check the Routes documentation:

-- Arnar (arnarbi at gmail)

Hosted by WebFaction

Log in as guest/cherrypy to create/edit wiki pages