Min 13-14 — Templates & Assets 🎨¶
Heaven renders HTML with Jinja2, asynchronously by default.
Static files¶
Point a URL prefix at a folder on disk:
app.ASSETS('assets') # serves ./assets at /assets/*
app.ASSETS('assets', '/static/*') # serves ./assets at /static/*
assets/logo.png is now at http://localhost:8000/assets/logo.png.
ASSETS stays inside its folder
The requested path is resolved before anything is opened, and requests that land outside the asset folder return 404, whether they got there via .. segments, an absolute path, or a symlink pointing out of the tree. .. segments that stay inside the folder still resolve normally. ASSETS is a wrapper over res.file(..., within=...), so the guarantees are the same ones documented in Serving Files.
In production, serve static files from Nginx, Caddy, or a CDN anyway. It is faster than routing them through the app.
Paths are relative to the working directory
By default the folder is resolved from wherever the process was started, which breaks when you run from a different directory. Anchor it to a file instead:
app.ASSETS('assets', relative_to=__file__)
Templates¶
app.TEMPLATES('templates')
Then render into the response body:
async def profile(req, res, ctx):
ctx.keep('user_name', 'Ada')
await res.render('profile.html', title='Your Profile')
| Method | Use when |
|---|---|
await res.render(name, **vars) | the normal case — async rendering |
res.renders(name, **vars) | templates configured with asynchronous=False |
await res.interpolate(name, **vars) | you want the rendered string back instead of setting the body |
TEMPLATES() also takes relative_to=__file__, an escape= override, and prefix= for namespacing (below).
What every template can see¶
Heaven injects the three request objects into every template automatically:
<h1>Hello, {{ ctx.user_name }}</h1>
<p>You are visiting: {{ req.url }}</p>
<p>Status: {{ res.status }}</p>
<title>{{ title }}</title> <!-- anything you passed to render() -->
So ctx doubles as your template context — anything a BEFORE hook stashed there is available in the template without being threaded through render().
Combining template folders¶
Call TEMPLATES() more than once and the folders are searched in order. Add a prefix to namespace a set — useful when mounting apps that each ship their own templates and might collide on index.html.
app.TEMPLATES('templates')
app.TEMPLATES('blog/templates', prefix='blog')
await res.render('index.html') # from ./templates
await res.render('blog/index.html') # from ./blog/templates
Next: Sharing state between hooks and handlers → Min 15-16 — The Context