Tag Archives: bottle

Jinja2 templates and Bottle

Although [Bottle’s][bottle] built-in mini-template language is remarkably useful, I nearly always prefer to use [Jinja2 templates][jinja2] because the syntax is very close to [Django’s template syntax][django] (which I am more familiar with) and because the Bottle template syntax for filling in blocks from a parent template is a bit limiting (but that’s kind of the point).

Bottle provides a nice jinja2_view decorator that makes it easy to use Jinja2 templates, but it isn’t that obvious how to configure the template environment for auto-escaping and default context, etc.

(The rest of this relates to Bottle version 0.11 and Jinja2 version 2.7.)

Template paths
————–

Bottle’s view decorator takes an optional `template_lookup` keyword argument. The default is to look in the current working directory and in a ‘views’ directory, i.e. `template_lookup=(‘.’, ‘./views/’)`.

You can override the template path like so:

from bottle import jinja2_view, route

@route(‘/’, name=’home’)
@jinja2_view(‘home.html’, template_lookup=[‘templates’])
def home():
return {‘title’: ‘Hello world’}

Which will load `templates/home.html`.

Most likely you will want to use the same template path for every view, which can be done by wrapping `jinja2_view`:

import functools
from bottle import jinja2_view, route

view = functools.partial(jinja2_view, template_lookup=[‘templates’])

@route(‘/’, name=’home’)
@view(‘home.html’)
def home():
return {‘title’: ‘Hello world’}

@route(‘/foo’, name=’foo’)
@view(‘foo.html’)
def foo():
return {‘title’: ‘Foo’}

That would have loaded `templates/home.html` and `templates/foo.html`.

Another way of setting a global template path for the view decorator is to fiddle with Bottle’s global default template path:

from bottle import TEMPLATE_PATH, jinja2_view, route

TEMPLATE_PATH[:] = [‘templates’]

@route(‘/’, name=’home’)
@jinja2_view(‘home.html’)
def home():
return {‘title’: ‘Hello world’}

N.B. I used `TEMPLATES_PATH[:]` to update the global template path directly rather than re-assigning it with `TEMPLATE_PATH = [‘templates’]`.

Template defaults
—————–

Bottle has a useful `url()` function to generate urls in your templates using named routes. But it isn’t in the template context by default. You can modify the default context on the Jinja2 template class provided by Bottle:

from bottle import Jinja2Template, url

Jinja2Template.defaults = {
‘url’: url,
‘site_name’: ‘My blog’,
}

Jinja2 version 2.7 by default does *not* escape variables. This surprises me, but it is easy to configure a template environment to auto-escape variables.

from bottle import Jinja2Template

Jinja2Template.settings = {
‘autoescape’: True,
}

Any of [the Jinja2 environment keyword arguments][environment] can go in this settings dictionary.

Using your own template environment
———————————–

Bottle’s template wrappers make a new instance of a Jinja2 template environment for each template (although if two views use the same template then they will share the compiled template and its environment).

You can avoid this duplication of effort by creating the Jinja2 template environment yourself, however this approach means you also need to write your own view decorator to use the custom template environment. No biggie.

Setting up a global Jinja2 template environment to look for templates in a “templates” directory:

from bottle import url
import jinja2

env = jinja2.Environment(
loader=jinja2.FileSystemLoader(‘templates’),
autoescape=True,
)
env.globals.update({
‘url’: url,
‘site_name’: ‘My blog’,
})

You then need a replacement for Bottle’s view decorator that uses the previously configured template environment:

import functools

# Assuming env has already been defined in the module’s scope.
def view(template_name):
def decorator(view_func):
@functools.wraps(view_func)
def wrapper(*args, **kwargs):
response = view_func(*args, **kwargs)

if isinstance(response, dict):
template = env.get_or_select_template(template_name)
return template.render(**response)
else:
return response

return wrapper

return decorator

@route(‘/’, name=’home’)
@view(‘home.html’)
def home():
return {‘title’: ‘Hello world’}

Conclusion
———-

It’s easy to customize the template environment for Jinja2 with Bottle and keep compatibility with Bottle’s own view decorator, but at some point you may decide it is more efficient to by-pass things and setup a custom Jinja2 environment.

Bottle is nice like that.

[bottle]: http://bottlepy.org/
[jinja2]: http://jinja.pocoo.org/
[django]: https://www.djangoproject.com/
[environment]: http://jinja.pocoo.org/docs/api/#jinja2.Environment

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()

@app1.route(‘/’)
def app1_home(): return “Hello World from App1”

app2 = bottle.Bottle()
@app2.route(‘/’)
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
else:
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.

[1]: http://blog.nturn.net/?p=289
[2]: http://jason.cleanstick.net/post/19943282016/stupid-simple-api-reference-for-bottle-py-web-services
[3]: https://github.com/bottlepy/bottle/commit/9b24401605e0470388a65c80a0964cba2bf64caf
[4]: http://bottlepy.org/

Class-based views for Bottle

I’m not convinced this is actually a good idea, but I have an approach for using class-based views as handlers for a route with [Bottle][bottle].

_(If you were mad keen on [Django’s][django] shift to [class-based views][cbv] you might reckon life wouldn’t be complete with a Bottle-based application until you employ classes for views. However Bottle’s use of decorators for tying URLs to views means it is less a natural fit than the same thing in Django.)_

The problem is that you can’t just decorate the method in your class using [`bottle.route`][route] because if you use that decorator on a method in a class you are telling Bottle to use the method before it has been bound to an instance of that class.

So although I wish it did, the following example will not work:

import bottle

class ViewClass(object):
@bottle.route(“/”)
def home_view(self):
return “My home page.”

obj = ViewClass()
bottle.run()

Running that will lead to errors about not enough arguments passed to the view method of your `ViewClass` instance.

Instead you need to register the route right after the object is created. This can be done in [the class’s `__new__` method][new]:

import bottle

class ViewClass(object):
def __new__(cls, *args, **kwargs):
obj = super(ViewClass, cls).__new__(cls, *args, **kwargs)
bottle.route(“/”)(obj.home_view)
return obj

def home_view(self):
return “My home page.”

obj = ViewClass()
bottle.run()

It works. It isn’t that pretty. You could achieve exactly the same thing by explicitly passing the `obj.home_view` method to `bottle.route` _after_ the instance is created. The advantage to doing this in the `__new__` method is it will happen automatically whenever `ViewClass` is instantiated.

And if you go down this path then [you should be aware of threads][threads]. Hey! Nice threads! Also I have a cold.

[bottle]: http://bottle.paws.de/
[cbv]: http://docs.djangoproject.com/en/dev/topics/class-based-views/
[django]: http://www.djangoproject.com/
[route]: http://bottle.paws.de/docs/dev/api.html#routing
[new]: http://docs.python.org/reference/datamodel.html#object.__new__
[threads]: http://bottle.paws.de/docs/dev/tutorial.html#accessing-request-data

Bottle’s view decorator and default variables

[Bottle][bottle]’s [`@view` decorator][view] provides a simple way to designate a template to render an HTML page. Your view function just has to return a dictionary, and its contents can be accessed from the template using the `'{{ name }}’` syntax.

The `@view` decorator can also take keyword arguments. These are treated as default template variables – if the dictionary returned by your view function doesn’t have a key for one of the keyword arguments then the template will use the value passed into the decorator, like so:

from bottle import view

@view(‘default.html’, author=’David Buxton’)
def home():
return {‘title’: ‘Home page’}

That would render any instance of `'{{ author }}’` as `’David Buxton’`. And then you can have another view function that overrides the keywords by returning a different value in the dictionary:

from bottle import view

@view(‘default.html’, author=’David Buxton’)
def music():
return {‘title’: ‘Thalassocracy’, ‘author’: ‘Frank Black’}

And at that point I wonder what is the advantage of using keyword arguments with `@view`: you have to decorate each function separately, and if you want to override a keyword in your return dictionary then it would be easier not to specify the keyword in the first place.

Thus the real point of using keywords with the `@view` function is only apparent if you curry the `@view` decorator with keywords first so that you can re-use the curried decorator and avoid repeating yourself.

*Someday I will re-write the previous sentence. Until then, sorry.*

Instead of passing a default author each time as in the examples above, let’s make a new `@view` decorator (using Python’s [functools module][functools]) and then use that on each view function:

import functools
from bottle import view

view = functools.partial(view, author=’David Buxton’)

@view(‘default.html’)
def home():
return {‘title’: ‘Home page’}

@view(‘default.html’)
def music():
return {‘title’: ‘Thalassocracy’, ‘author’: ‘Frank Black’}

The new decorator means you get the default keyword arguments wherever you use `@view` while permitting any function to override those defaults in the dictionary it returns.

And if you wanted to get really lazy you could even pass in a template name when wrapping the decorator with `functools.partial`, however you would not be able to use your wrapped decorator to change the template name because it is a positional argument (like what [it explains here in the functools documentation][partial]). You would also have to call the decorator with no arguments like `’@defaultview()’`. So forget I mentioned it.

I’m not saying you are lazy.

[bottle]: http://bottle.paws.de/
[functools]: http://docs.python.org/library/functools.html
[partial]: http://docs.python.org/library/functools.html#functools.partial
[view]: http://bottle.paws.de/docs/0.8/api.html#bottle.view

Django-style routing for Bottle

[Bottle][bottle] provides the [`@route` decorator][route] to associate URL paths with view functions. This is very convenient, but if you are a [Django][django]-reject like me then you may prefer having all your URLs defined in one place, the advantage being it is easy to see at a glance [all the different URLs your application will match][urlconf].

*Updated: I have re-written this post and the example to make it simpler following Marcel Hellkamp’s comments (Marcel is the primary author of Bottle). My original example was needlessly complicated.*

It is possible to have [a Django-style urlpatterns stanza][urlpatterns] with a Bottle app. Here’s how it can work:

from bottle import route

# Assuming your *_page view functions are defined above somewhere
urlpatterns = (
# (path, func, name)
(‘/’, home_page, ‘home’),
(‘/about’, about_page, ‘about’),
(‘/contact’, contact_page, ‘contact’),
)

for path, func, name in urlpatterns:
route(path, name=name)(func)

Here we run through a list where each item is a triple of URL path, view function and a name for the route. For each we simply call the `route` method and then invoke it with the function object. Not as flexible as using the decorator on a function (because the `@route` decorator can take additional keyword arguments) but at least you can have all the routes in one place at the end of the module.

Then again if you have so many routes that you need to keep them in a pretty list you probably aren’t writing the simple application that Bottle was intended for.

(This was tested with Bottle’s 0.8 and 0.9-dev branches.)

[bottle]: http://bottle.paws.de/
[route]: http://bottle.paws.de/docs/0.8/api.html#bottle.route
[django]: http://www.djangoproject.com/
[urlconf]: http://docs.djangoproject.com/en/1.2/topics/http/urls/#example
[urlpatterns]: http://docs.djangoproject.com/en/dev/topics/http/urls/