Monthly Archives: May 2012

Custom template folders with Flask

Someone was asking on [Flask][flask]’s IRC channel [#pocoo][irc] about sharing templates across more than one app but allowing each app to override the templates (pretty much what [Django’s TEMPLATE_DIRS setting][django] is for). One way of doing this would be to customise the Jinja2 template loader.

Here’s a trivial Flask app that searches for templates first in the default folder (‘templates’ in the same folder as the app) and then in an extra folder.

import flask
import jinja2

app = flask.Flask(__name__)
my_loader = jinja2.ChoiceLoader([
app.jinja_loader = my_loader

def home():
return flask.render_template(‘home.html’)

if __name__ == “__main__”:

The only thing special here is creating a new template loader and then assigning it to [the `jinja_loader` attribute on the Flask application][attr]. [`ChoiceLoader`][choice] will search for a named template in the order of the loaders, stopping on the first match. In this example I re-used the loader that is created by default for an app, which is roughly like `FileSystemLoader(‘/path/to/app/templates’)`. There are [all kinds of other exciting template loaders available][loaders].

I really like the fact that Flask and Bottle’s APIs are so similar. Next I want Flask to include [Bottle’s template wrapping decorator][view] by default (there’s [a recipe in the Flask docs][templated]) and for both of them to re-name it `@template`.


Inspecting your routes in Bottle

Marcel Hellkamp [recently added a small feature][3] to Bottle that makes it easy to inspect an application’s routes and determine if a particular route is actually for a mounted sub-application.

([Bottle is a small module written in Python for making websites][4].)

Route objects (items in the `app.routes` list) now have extra information when the route was created by mounting one app on another, in the form of a new key `mountpoint` in `route.config`.

Here’s a trivial app with another app mounted on it:

import bottle

app1 = bottle.Bottle()

def app1_home(): return “Hello World from App1”

app2 = bottle.Bottle()
def app2_home(): return “Hello World from App2”

app1.mount(prefix=’/app2/’, app=app2)

And a utility function that returns a generator of prefixes and routes:

def inspect_routes(app):
for route in app.routes:
if ‘mountpoint’ in route.config:
prefix = route.config[‘mountpoint’][‘prefix’]
subapp = route.config[‘mountpoint’][‘target’]

for prefixes, route in inspect_routes(subapp):
yield [prefix] + prefixes, route
yield [], route

Finally, inspecting all the routes (including mounted sub-apps) for the root Bottle object:

for prefixes, route in inspect_routes(app1):
abs_prefix = ‘/’.join(part for p in prefixes for part in p.split(‘/’))
print abs_prefix, route.rule, route.method, route.callback

This new feature is sure to revolutionise everything.