Closed simonw closed 1 year ago
The call
mechanism is interesting:
{% macro render_dialog(title, class='dialog') -%}
<div class="{{ class }}">
<h2>{{ title }}</h2>
<div class="contents">
{{ caller() }}
</div>
</div>
{%- endmacro %}
{% call render_dialog('Hello World') %}
This is a simple dialog rendered by using a macro and
a call block.
{% endcall %}
{% macro dump_users(users) -%}
<ul>
{%- for user in users %}
<li><p>{{ user.username|e }}</p>{{ caller(user) }}</li>
{%- endfor %}
</ul>
{%- endmacro %}
{% call(user) dump_users(list_of_user) %}
<dl>
<dt>Realname</dt>
<dd>{{ user.realname|e }}</dd>
<dt>Description</dt>
<dd>{{ user.description }}</dd>
</dl>
{% endcall %}
Submitted a PR:
https://jinja.palletsprojects.com/en/3.1.x/templates/#assignments
Assignments at top level (outside of blocks, macros or loops) are exported from the template like top level macros and can be imported by other templates.
The namespace mechanism is interesting - for working around the fact that assignments can't be seen from outside of their block:
{% set ns = namespace(found=false) %}
{% for item in items %}
{% if item.check_something() %}
{% set ns.found = true %}
{% endif %}
* {{ item.title }}
{% endfor %}
Found item having something: {{ ns.found }}
I didn't know about block assignments!
{% set navigation %}
<li><a href="/">Index</a>
<li><a href="/downloads">Downloads</a>
{% endset %}
Also support filters:
{% set reply | wordwrap %}
You wrote:
{{ message }}
{% endset %}
{% include ['special_sidebar.html', 'sidebar.html'] ignore missing %}
That will try to include the first, then the second template and fail silently if none are found.
Import is interesting for macros: https://jinja.palletsprojects.com/en/3.1.x/templates/#import
{% import 'forms.html' as forms %}
<dl>
<dt>Username</dt>
<dd>{{ forms.input('username') }}</dd>
<dt>Password</dt>
<dd>{{ forms.input('password', type='password') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>
OR
{% from 'forms.html' import input as input_field, textarea %}
Macros and variables starting with one or more underscores are private and cannot be imported.
Lists, tuples and dictionaries are valid Jinja literals.
Bit surprising (an old bug I think):
Unlike Python, chained pow is evaluated left to right.
{{ 3**3**3 }}
is evaluated as(3**3)**3
in Jinja, but would be evaluated as3**(3**3)
in Python. Use parentheses in Jinja to be explicit about what order you want.
~
"Converts all operands into strings and concatenates them."
{{ "Hello " ~ name ~ "!" }}
If expressions are neat: https://jinja.palletsprojects.com/en/3.1.x/templates/#if-expression
{% extends layout_template if layout_template is defined else 'default.html' %}
{{ "[{}]".format(page.title) if page.title }}
Lots of built-in filters: https://jinja.palletsprojects.com/en/3.1.x/templates/#list-of-builtin-filters
I like batch
:
<table>
{%- for row in items|batch(3, ' ') %}
<tr>
{%- for column in row %}
<td>{{ column }}</td>
{%- endfor %}
</tr>
{%- endfor %}
</table>
That second optional argument is used to fill in missing items.
There's a |filesizeformat
filter!
I didn't need https://github.com/simonw/datasette/blob/5aa359b86907d11b3ee601510775a85a90224da8/datasette/utils/__init__.py#L849-L858 after all.
Users on this page: {{ users|map(attribute='username')|join(', ') }}
Users on this page: {{ titles|map('lower')|join(', ') }}
|pprint
looks useful.
These test ones are cute:
{{ numbers|reject("odd") }}
{{ numbers|select("divisibleby", 3) }}
{{ numbers|select("lessthan", 42) }}
I wonder how good |striptags
is?
Looks like it uses markupsafe
under the hood, implemented here: https://github.com/pallets/markupsafe/blob/33307792f69e9e6bd9589919620300985955800c/src/markupsafe/__init__.py#L155-L166
Which uses these two regexes:
_strip_comments_re = re.compile(r"<!--.*?-->", re.DOTALL)
_strip_tags_re = re.compile(r"<.*?>", re.DOTALL)
The |tojson
filter says: https://jinja.palletsprojects.com/en/3.1.x/templates/#jinja-filters.tojson
The returned string is safe to render in HTML documents and
<script>
tags. The exception is in HTML attributes that are double quoted; either use single quotes or the|forceescape
filter.
Implementation: https://github.com/pallets/jinja/blob/52843b5cbf635b37a82ac0b6c901921a8ee076ff/src/jinja2/utils.py#L657-L663
return markupsafe.Markup(
dumps(obj, **kwargs)
.replace("<", "\\u003c")
.replace(">", "\\u003e")
.replace("&", "\\u0026")
.replace("'", "\\u0027")
)
|urlencode
is handy:
Basic wrapper around
urllib.parse.quote()
when given a string, orurllib.parse.urlencode()
for a dict or iterable.
You can test for if a filter is available or not:
{% if 'markdown' is filter %}
{{ value | markdown }}
{% else %}
{{ value }}
{% endif %}
It has lorem ipsum!
jinja-globals.lipsum(n=5, html=True, min=20, max=100)
Generates some lorem ipsum for the template. By default, five paragraphs of HTML are generated with each paragraph between 20 and 100 words. If html is False, regular text is returned. This is useful to generate simple contents for layout testing.
https://jinja.palletsprojects.com/en/3.1.x/templates/#i18n looks very handy:
{% trans %}Hello, {{ user }}!{% endtrans %}
{% trans user=user.username %}Hello, {{ user }}!{% endtrans %}
{% trans book_title=book.title, author=author.name %}
This is {{ book_title }} by {{ author }}
{% endtrans %}
{% trans count=list|length %}
There is {{ count }} {{ name }} object.
{% pluralize %}
There are {{ count }} {{ name }} objects.
{% endtrans %}
When translating blocks of text, whitespace and linebreaks result in hard to read and error-prone translation strings. To avoid this, a trans block can be marked as trimmed, which will replace all linebreaks and the whitespace surrounding them with a single space and remove leading and trailing whitespace.
{% trans trimmed book_title=book.title %}
This is {{ book_title }}.
You should read it!
{% endtrans %}
{{ _("Hello, World!") }}
{{ _("Hello, %(user)s!")|format(user=user.username) }}
Interesting extension: https://jinja.palletsprojects.com/en/3.1.x/templates/#expression-statement
{% do navigation.append('a string') %}
<pre>{% debug %}</pre>
Works if you turn on the extension: https://jinja.palletsprojects.com/en/3.1.x/templates/#debug-statement
This used to be an extension but is now built in:
{% with %}
{% set foo = 42 %}
{{ foo }} foo is 42 here
{% endwith %}
foo is not visible here any longer
This too:
{% autoescape true %}
Autoescaping is active within this block
{% endautoescape %}
{% autoescape false %}
Autoescaping is inactive within this block
{% endautoescape %}
Extensions: https://jinja.palletsprojects.com/en/3.1.x/extensions/
Mainly i18n - the others are pretty tiny. You can write your own.
https://jinja.palletsprojects.com/en/3.1.x/extensions/#module-jinja2.ext
By writing extensions you can add custom tags to Jinja. This is a non-trivial task and usually not needed as the default tags and expressions cover all common use cases. The i18n extension is a good example of why extensions are useful. Another one would be fragment caching.
Extensions are pretty low-level AST and parser code.
https://jinja.palletsprojects.com/en/3.1.x/integration/#babel describes Babel integration, for extracting translatable strings.
https://jinja.palletsprojects.com/en/3.1.x/switching/ has some short notes on Django template comparisons.
https://jinja.palletsprojects.com/en/3.1.x/faq/ - only 3 FAQs there.
And I'm done!
I use Jinja enough that I should really do a full read-through of the docs to see what I've missed.
https://jinja.palletsprojects.com/en/3.1.x/