Skip to content

Building layouts and using helpers

Combine Jinja syntax with Orionis services while keeping business logic outside presentation.

Templates use Jinja2, with {{ value }} expressions and {% ... %} statements. Prepare data in a controller or service; avoid expensive queries inside each iteration. The async environment automatically awaits async callables: write {{ route("users.edit", id=user.id) }}. Python’s await keyword is not part of template expressions.

Create resources/views/layouts/base.html:

<!doctype html>
<html lang="en">
<head><title>{% block title %}Reports{% endblock %}</title></head>
<body>
{% include "partials/navigation.html" %}
<main>{% block content %}{% endblock %}</main>
</body>
</html>

Create resources/views/partials/navigation.html with your navigation and resources/views/reports/index.html with:

{% extends "layouts/base.html" %}
{% block title %}Recent reports{% endblock %}
{% block content %}
<ul>
{% for report in reports %}
<li>{{ report.title }}</li>
{% else %}
<li>No reports yet.</li>
{% endfor %}
</ul>
{% endblock %}

Return await View.make("reports.index", reports=reports). extends reuses the layout and block replaces a region; include inserts another template. A loop’s {% else %} handles empty collections. Use Jinja macros for parameterized fragments; Orionis adds no proprietary component syntax or Blade directives.

For a complete local demonstration, place this handler and registration in routes/web.py:

from orionis.support.facades import Route, View
async def reports_index():
reports = [{"title": "October report"}]
return await View.make("reports.index", reports=reports)
Route.get("/reports", reports_index)

GET /reports renders the layout with one report. Replacing the sample list with [] displays No reports yet.. The layout’s included navigation file must exist; omitting it raises a template error.

The following names are registered during ViewServiceProvider.boot(). Helpers resolving requests, sessions, or services need those components active; constructing an isolated Jinja2Engine is insufficient.

TaskAvailable helpers
Routes and linksroute(name, **params), url(path="/", **query), secure_url(path="/", **query)
Assetsasset(path, disk=None), secure_asset(path, disk=None); default disk public.
Authenticationauth() returns the facade; auth().user(), .check(), .guest().
Request / sessionrequest(), session(); may return None outside scope.
Formsold(key, default=None), errors.any/has/get/first/all, flash(key, default=None)
CSRFcsrf_token(), csrf_field(), {% csrf %}; hidden field name _csrf.
Translationtrans(key, locale=None, **replace), __(...), choice(key, count, locale=None, **replace), locale(), locales()
Global stateapp(), config(key, default=None), cache(key, default=None)
Data and diagnosticscollect(value=None), stringable(value=""), dump(*values)
Dates/versionsnow(), today(), framework_version(), python_version()
Encryptionencrypt(plaintext), decrypt(payload)

route path values are encoded; remaining parameters become a query string. Missing routes or required arguments raise ViewRouteException. URLs use the current request’s origin when available and may be relative outside scope. secure_url upgrades to HTTPS when a host exists; it does not discover a host for a relative path. asset asks the disk for file.url(): it neither checks existence nor publishes the file.

cache(key, default) treats None as a miss. errors is a bag with methods, not a dict. old returns an empty string for None. auth() does not cache identity and preserves request scope; check auth().check() before accessing user attributes. For concurrent locales, explicitly pass locale= because locale() reads shared global state.

Application autoescaping defaults to enabled. Keep escaping for {{ user.name }}; use |safe and Markup only for HTML you control. The markdown filter returns an HTML str without sanitizing arbitrary content. Marking it safe can admit user-supplied HTML; add a sanitization policy if your product accepts external Markdown.

json serializes with msgspec, optionally |json(indent=2), but falls back to str(value) when encoding fails. It is a presentation filter, not a guarantee of valid JSON for every object or a <script>-specific escaping mechanism. Avoid interpolating it as executable code. dump() exposes values as HTML and belongs in controlled diagnostics; never dump secrets. Encryption helpers do not replace authorization.


Index · auth · localization · storage · session