Using Routes with TurboGears

Status: Draft

Routes is a Python re-implementation of the Rails routes system for mapping URL's to Controllers/Actions and generating URL's. Routes makes it easy to create pretty and concise URL's that are RESTful with little effort.

This is a preliminary integration of routes which is implimented on an app by app basis. No magical discovery of controllers is available yet, but I plan on implimenting this in the near future.

Installing Routes

Installing routes is extremely simple:

easy_install routes

The usual caveats regarding sudo and --script-dir apply

The root controller

Your root controller will need to look like this:

class Root(controllers.RootController):

    def __init__(self):
        self.base_re = re.compile( r'^(?P<protocol>[a-zA-Z]+)://(?P<host>.*)' )

        # Change the controllers here to the controllers in your app:
        self.controllers = {'pickup': Pickup(), 'main': Main()}

        # Here you need to add your own routes
        # (http://routes.groovie.org/manual.html)
        m.connect(':controller/:action', controller='main', action='index')
        m.connect('pickup/:id/:action', controller='pickup', action='prepare',
            requirements=dict(id='\d{1,11}'))

        m.create_regs(self.controllers.keys())

    def redirect(self, url):
        # Recreated this function as the cherrypy one is depreciated
        raise cherrypy.HTTPRedirect(turbogears.url(url), 302)


    @turbogears.expose()
    def default(self, *args, **kwargs):

        base = cherrypy.request.base
        d = self.base_re.match( base ).groupdict()
        host = d[ 'host' ]
        proto = d[ 'protocol' ]
        path = cherrypy.request.path

        con = request_config()
        con.mapper = m # the Routes Mapper object
        con.host = host
        con.protocol = proto
        con.redirect = self.redirect

        # I (MichaelGundlach) had to add the following line in order
        # to support the "condition" parameter to a route; i.e., if
        # you wish to specify that the route only allows certain
        # HTTP methods like DELETE.  Without this line, Routes
        # doesn't see any environ info and doesn't match your path.
        m.environ = {'REQUEST_METHOD': cherrypy.request.method}

        con.mapper_dict = m.match( path )

        if con.mapper_dict:
            c = con.mapper_dict.pop( 'controller' )
            controller = self.controllers[c]
            action = con.mapper_dict.pop( 'action', 'index' )
            try:
                meth = getattr(controller, action, getattr(controller,
                    'default'))
            except AttributeError:
                raise cherrypy.NotFound( path )
            kwargs.update( con.mapper_dict )

            return meth( **kwargs )

        raise cherrypy.NotFound( path )

You will also need to add the follwing lines to the top of your controllers.py:

from routes import *
import re
import cherrypy
# Create the mapper object
m = Mapper()

Note that you don't need to connect your controllers to the root controller as you would usually do. The connection of the required controller is done on an ad hoc basis.

I hope to update this using a subclassable RoutesController (which itself will be a subclass of RootController) and a few other bits in the TG internals that will allow you to enable and define routes in the .cfg files.


The comment feature has been disabled on this page due to heavy spamming. If you want to comment on the contents of this page, if you have questions, or want to report an error, please write to the TurboGears mailing list.

Past comments:

localhost
2007-05-16 09:34:23

getattr(controller, action, getattr(controller, 'default')) will fail if controller doesn't have a 'default' method, even if it does have action. The inner getattr is evaluated before the outer one is called (not only if action isn't found).

This might be the preferred behaviour, but in case you dont want 'default' functions on your controllers just change this to:
meth = getattr(controller, action, getattr(controller, 'default', None))

1.0/RoutesIntegration (last edited 2008-11-18 22:23:11 by MichaelGundlach)