Templates¶
Veloce renders HTML with Jinja2. You can
use the class-based Jinja2Templates
API explicitly, or the Flask-style render_template
shortcut once a template folder is configured on the app. Jinja2 ships with
Veloce as a runtime dependency, so pip install veloceframework is all you need.
A first template¶
Create a templates/ directory next to your app module with a file
hello.html:
Construct a Jinja2Templates pointed at that directory and return a rendered
response from a handler:
from veloce import Jinja2Templates, Request, Veloce
app = Veloce()
templates = Jinja2Templates(directory="templates")
@app.get("/hello/{name}")
async def hello(request: Request, name: str):
return templates.TemplateResponse(
"hello.html",
{"request": request, "name": name},
)
TemplateResponse(name, context) renders the named template against the
context dict and returns an HTMLResponse.
The context is a plain dictionary — every variable the template uses must be a
key in it.
name may also be a list of candidate template names; the first one that
exists on disk is rendered, so you can fall back from a specific template to a
generic one. The same applies to render_template, stream_template, and
get_template:
In production (auto_reload disabled) the resolved candidate list is cached so
the on-disk lookup runs once per distinct list. The cache is bounded by the
Jinja2Templates.RESOLVED_CACHE_MAX class attribute (default 1024, FIFO
eviction of the oldest entry).
Override it on the class or instance if you build fallback lists from many request-derived names:
TemplateResponse also accepts two optional keyword arguments:
| Argument | Effect |
|---|---|
media_type=... |
Override the Content-Type (it defaults to text/html). |
background=... |
Attach a background task — a callable, a BackgroundTask, or a BackgroundTasks — that runs after the response is sent. |
Note
Pass the request object in the context ({"request": request, ...}) as
shown above. This mirrors the convention used throughout Veloce's own
examples and keeps the request available to templates and context
processors. See Requests and responses.
Configuring the template folder on the app¶
The Flask-style helpers — render_template,
render_template_string,
and stream_template — look up a
Jinja2Templates instance bound to the active application. The simplest way to
bind one is to pass template_folder to the Veloce constructor:
from veloce import Request, Veloce, render_template
app = Veloce(template_folder="templates")
@app.get("/hello/{name}")
async def hello(request: Request, name: str):
return render_template("hello.html", name=name)
When template_folder is relative, it is resolved against the app's package
directory (the folder containing the module named by import_name), so the
location does not depend on the current working directory.
render_template(template_name, **context) takes the context as keyword
arguments and returns the rendered string. Returning a string from a handler
produces an HTML response, so no explicit wrapping is needed for the common
case. If you need to set a status code or headers, wrap the result yourself:
from veloce import HTMLResponse, Request, Veloce, render_template
app = Veloce(template_folder="templates")
@app.get("/missing")
async def missing(request: Request):
body = render_template("404.html")
return HTMLResponse(content=body, status_code=404)
Warning
render_template, render_template_string, and stream_template require
an active application context. Calling them outside a request handler (or
without a bound Jinja2Templates) raises RuntimeError. Inside a handler
you are always within an app context.
Inline string templates¶
render_template_string
renders a template from a source string rather than a file. It is convenient
for short, one-off fragments:
from veloce import Request, Veloce, render_template_string
app = Veloce()
@app.get("/greet/{name}")
async def greet(request: Request, name: str):
return render_template_string("<p>Hi, {{ name }}</p>", name=name)
Unlike render_template, this helper falls back to a transient Jinja
environment when no template folder is configured, so it works even without
template_folder=. When a Jinja2Templates instance is bound, it honours the
app's registered filters, globals, and context processors.
Autoescaping¶
By default Jinja2Templates autoescapes templates whose name ends in .html,
.htm, .xhtml, or .xml. Variables interpolated into those templates are
HTML-escaped, which protects against cross-site scripting.
To render a value as raw HTML, mark it safe with Markup:
from veloce import Markup, Request, Veloce, render_template_string
app = Veloce()
@app.get("/raw")
async def raw(request: Request):
snippet = Markup("<em>emphasised</em>")
return render_template_string("{{ snippet }}", snippet=snippet)
Warning
Only wrap content you trust in Markup. Marking user-supplied input as safe
defeats autoescaping and reintroduces XSS risk.
Pass autoescape= to the constructor to override the default policy with a
boolean or a callable:
from veloce import Jinja2Templates
templates = Jinja2Templates(directory="templates", autoescape=True)
Built-in template globals¶
When a template is rendered inside a request, Veloce makes four names available without adding them to the context:
url_for— build a URL for a named route, e.g.{{ url_for('hello', name='world') }}.g— the per-request application-context object.current_app— the active application.get_flashed_messages— read the messages queued withflash(), e.g.{% for m in get_flashed_messages() %}{{ m }}{% endfor %}. It returns an empty list outside a request.
The route name passed to url_for is the handler's function name unless you
override it. See Routing for reverse URL generation.
Filters, globals, and tests¶
Register custom Jinja filters, globals, and tests on the application. They become available in every render that runs inside the app's request scope.
from veloce import Request, Veloce, render_template_string
app = Veloce(template_folder="templates")
@app.template_filter("shout")
def shout(value: str) -> str:
return value.upper() + "!"
@app.template_global("app_name")
def app_name() -> str:
return "Veloce"
@app.get("/demo")
async def demo(request: Request):
return render_template_string("{{ 'hi' | shout }} from {{ app_name() }}")
The name argument is optional and defaults to the function's own name. Each
decorator has an imperative counterpart — app.add_template_filter(func, name),
app.add_template_global(func, name), and app.add_template_test(func, name) —
for registering callables you did not define inline.
Context processors¶
A context processor is a callable that runs before each render and returns a dict merged into every template's context. Use it to inject values that should appear in many templates without repeating them in every handler:
from veloce import Request, Veloce, render_template_string
app = Veloce(template_folder="templates")
@app.context_processor
def inject_site():
return {"site_name": "Veloce Docs"}
@app.get("/about")
async def about(request: Request):
return render_template_string("Welcome to {{ site_name }}")
Processors run in registration order and their dicts are merged. The explicit context you pass to a render call wins over a processor's value on a key collision.
Accessing the Jinja environment¶
When templating is configured, app.jinja_env exposes the underlying Jinja
Environment for advanced configuration, and app.jinja_loader returns its
loader (or None when no templating is set up):
from veloce import Veloce
app = Veloce(template_folder="templates")
app.jinja_env.trim_blocks = True
app.jinja_env.lstrip_blocks = True
app.jinja_env raises RuntimeError if no template folder has been configured.
Streaming large templates¶
For a response whose body is large or expensive to build, render it
incrementally instead of buffering the whole string. stream_template
returns an iterator of string chunks; wrap it in a
StreamingResponse:
from veloce import Request, StreamingResponse, Veloce, stream_template
app = Veloce(template_folder="templates")
@app.get("/report")
async def report(request: Request):
rows = range(10000)
return StreamingResponse(stream_template("report.html", rows=rows))
The chunks render lazily as the response body is consumed. Veloce captures the
request context so template globals such as url_for resolve correctly during
streaming, even though the body is emitted after the handler returns.
Asynchronous rendering¶
Jinja2Templates also offers render_async, which uses a separate
async-enabled Jinja environment so templates performing async I/O during
{% include %} resolve without blocking the event loop:
from veloce import HTMLResponse, Jinja2Templates, Request, Veloce
app = Veloce()
templates = Jinja2Templates(directory="templates")
@app.get("/async")
async def async_page(request: Request):
html = await templates.render_async("hello.html", {"name": "async"})
return HTMLResponse(content=html)
The async environment is built lazily on first use, so apps that never render asynchronously pay nothing for it.
Next steps¶
- Serve CSS, JavaScript, and images referenced by your templates — see Static files.
- Build links inside templates with
url_for— see Routing. - Use
g,flash, andcurrent_appfrom templates and handlers — see Flask-style helpers. - Full signatures are in the API reference.