Routing

Routing is what matches a url (such as /posts/123) to a controller and action (such as Posts.view(123)). Ferris can automatically generate routes for your actions but also provides the flexibility for you to specify your own routes.

It’s useful to think of the route in three components:

  • The route name is a unique, canonical name for the action. This is used for inter-app references to other routes. For example posts-view.
  • The route function refers to the controller and action. For example Posts.view().
  • The route url is how the function can be called from the outside world. For example /posts/view/123.

Parts

A route is automatically generated using four parts determined from the route function:

  • The name of the controller
  • The prefix, if specified
  • The name of the action
  • The parameters of the action, if any

These are called the route parts and are used to build both the route name and the route url from the route function.

URL and Name Generation

For the route name, it follows the convention [prefix:]handler:action with handler being underscored.

For the url, it follows the convention [/prefix]/handler/action[/param_1, etc.] with handler being underscored.

The following table demonstrates various mappings:

Action URL name
Time.stop() /time/stop time:stop
Daleks.exterminate(who) /daleks/exterminate/<who> daleks:exterminate
Numbers.range(min, max) /numbers/range/<min>/<max> numbers:range
Spaceships.xml_specs() /xml/spaceships/specs xml:spaceships:specs
UserComments.json_stats /json/user_comments/stats json:user_comments:stats

CRUD Actions

The methods named list, view, add, edit, and delete are always treated as actions and are implicitly routed (even when prefixed). This means you don’t have to use the @route decorator to expose these actions and there’s no way to unexpose these.

These methods have preset url mappings as follows, but can be prefixed:

Action URL
list /controller
add /controller/add
view /controller/:<key>
edit /controller/:<key>/edit
delete /controller/:<key>/delete

Non-CRUD Actions

Actions other than the CRUD actions need to be explicitly routed using @route or @route_with.

Take the following methods for example:

def list(self):
    return 'list'

@route
def test(self):
    return 'test'

def run(self):
    return 'run'

The methods list and test will be accessible via HTTP, but the method run is only accessible from within your code.

ferris.core.controller.route(f)[source]

Marks a method for automatically routing and accessible via HTTP. See routing for more details on how methods are auto-routed. This decorator should always be the outermost decorator.

For example:

@route
def exterminate(self):
    return 'EXTERMINAAATE!'

To set a custom url or name for an action, use @route_with

ferris.core.controller.route_with(*args, **kwargs)[source]

Marks a class method to be routed similar to route() and passes and additional arguments to the webapp2.Route constructor.

Parameters:template – Sets the URL template for this action

For example:

@route_with(template='/posts/archive/<year>')
def archive_by_year(self, year):
    pass

Prefixes

A prefix is just a simple string that is placed before the action name, route name and url. It’s useful for grouping actions. You might use the ‘admin’ prefix to group together actions for an administrator and the ‘api’ prefix to group together actions that return machine-readable data instead of html.

Prefixes must be explicitly listed in the prefixes property in the Meta configuration for a Controller. For example:

class Posts(Controller):
    class Meta:
        prefixes = ('json', 'admin')

    @route
    def json_stats(self):
        pass

    @route
    def xml_stats(self):
        pass

json_stats will have the url /json/posts/stats but xml_stats will be at /posts/xml_stats because there isn’t a prefix setup for ‘xml’.

Generating URLs to Actions

There is a standard way to generate URLs to actions across the application:

Controller.uri(route_name = None, prefix = <sentinel>, controller = <sentinel>, action = <sentinel>, _pass_all = False, _full = False, *args, **kwargs)

Generate in-application URIs (or URLs).

Parameters:
  • route_name – The route name for which to generate a URI for, if not provided then prefix, controller, and action will be used to determine the route name
  • prefix – The prefix of the desired URI, if omitted then the current prefix is used.
  • controller – The controller name of the desired URI, if omitted then the current controller is used.
  • action – The action name of the desired URI, if omitted then the current action is used.
  • _pass_all – will pass all current URI parameters to the generated URI (useful for pagination, etc.)
  • _full – generate a full URI, including the hostname.
  • kwargs – arguments passed at URL or GET parameters.

Examples:

uri('foxes:run') # -> /foxes/run
uri(prefix=False, controller='foxes', action='run')  # -> /foxes/run

# when currently at /foxes/run
uri(action='hide') # -> /foxes/hide

Attempting to generate a URL to an action that doesn’t exist will result in an exception.

Checking if an action exists

You can check for the existence of an action before attempting to generate a URL to it:

Controller.uri_exists(route_name = None, prefix = <sentinel>, controller = <sentinel>, action = <sentinel>, *args, **kwargs)

Check if a route exists.

You can see if you’re on a particular action. While this may seem like a superfluous feature, it’s very useful in templates:

Controller.on_uri(route_name = None, prefix = <sentinel>, controller = <sentinel>, action = <sentinel>, *args, **kwargs)

Checks to see if we’re currently on the specified route.

Manual Routing

Your application’s route configuration is located in app/routes.py. You can configure custom routes such as the application root and additional redirects. Plugins can also be enabled using this configuration file.

Root

By default, Ferris shows a helpful landing page when you pull up /. However, in your complete application, you will want this to go to one of your handlers. The easiest way to do this is to remove this:

# Default root route
routing.default_root()
ferris.core.routing.default_root(app_router=None)[source]

Adds the default Ferris root route

Replace it with something like this:

# Default root route
routing.redirect('/', to='/posts')
ferris.core.routing.redirect(url, to, app_router=None)[source]

Adds a redirect route with the given url templates.

Custom

Beyond redirects, you can also create completely custom routes. This is very useful for when you want to expose an ordinary webapp2.RequestHandler or similar:

routing.add(routing.Route('/special', SpecialHandler))
ferris.core.routing.add(route, app_router=None)[source]

Adds a webapp2.Route class to the router

Controllers

The default routes.py automatically routes all of the controllers in the app using auto_route:

# Routes all App handlers
routing.auto_route()
ferris.core.routing.auto_route(app_router=None, plugin=None)[source]

Automatically routes all controllers in main app or the given plugin

You can remove this and route manually using route_controller:

from app.controllers.posts import Posts
routing.route_controller(Posts)
ferris.core.routing.route_controller(controller_cls, app_router=None)[source]

Adds all of the routes for the given controller