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.
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:
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:
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.
To set a custom url or name for an action, use @route_with
- ferris.core.controller.route_with(*args, **kwargs)¶
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
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).
- 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.
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.
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()
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)¶
Adds a redirect route with the given url templates.
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:
- ferris.core.routing.add(route, app_router=None)¶
Adds a webapp2.Route class to the router
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)¶
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)¶
Adds all of the routes for the given controller