Using Jinja with Air
We love Jinja. Proven and fast, its our go-to for when we want to manually craft templates containing programmatic content. To that end, we've ensured Air works great at combining Jinja and Air Tags together.
Note
This document covers the concepts and how Jinja2 works in Air. The full reference for the tooling can be found at the Templates API Reference.
"Jinja or Jinja2?"
While the package is listed on PyPI as Jinja2, that package and the official Jinja docs refers to Jinja as just "Jinja". Also, Jinja was released in 2008 and is well into the 3.x release cycle. If we want to lean into pedantry, we are arguably using Jinja 5 (base plus major releases cycles of 0.x, 1.x, 2.x, and 3.x).
Most importantly, it is the intent of the maintainer of Jinja to not only document the package as 'Jinja' but to even provide a
jinja
namespace in addition tojinja2
.In short, to match the Jinja documentation and the intent of the maintainer, in the Air documentation we use the term "Jinja".
Using Jinja for the HTML Layout
Air Tags are powerful but for those of us with a lot of experience with HTML, sometimes its easy to construct layouts using Jinja. As it is closer in look-and-feel to HTML for some of us that makes ensuring the end result looks good is easier.
Here's a simple Jinja layout file:
<!doctype html>
<html>
<head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" />
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/htmx.min.js"
integrity="sha384-Akqfrbj/HpNVo8k11SXBb6TlBWmXXlYQrCSqEWmyKJe+hDm3Z/B2WVG4smwBkRVm"
crossorigin="anonymous"></script>
<title>{{title}}</title>
</head>
<body>
<main class="container">
{# We need to safe the content, which can be
a security risk. We'll cover mitigation
of such issues later on this page.
#}
{{content|safe}}
</main>
</body>
</html>
If you've used Jinja before this should look familiar. Now let's add in our Air Tags-powered content, which we'll do from the view.
import air
app = Air()
# Set the Jinja render function
jinja = air.JinjaRenderer(directory="tests/templates")
@app.get('/')
def index(request: Request):
content = air.Main(
air.h2('Does Jinja work with Air Tags?')
air.P("Jinja works great with Air Tags")
)
return jinja(
request,
name="base.html",
title="FAQ",
content=content,
)
When run, this will look like this:
TODO: ADD SCREEN CAPTURE
Why don't we call the
jinja()
functionrender()
?Because Air uses
.render()
as a method name in a lot of places, especially with Air Tags. So even thoughrender()
instead ofjinja()
is more semantically correct, to avoid confusion and collision, we usejinja2()
instead.