📌 変数 (Variables)
テンプレート内でPythonの変数を展開します。
目的 | Jinja2 構文 | 説明 |
---|---|---|
基本的な変数の表示 | {{ my_variable }} |
コンテキストから渡された変数の値を表示します。 |
辞書の値へのアクセス | {{ my_dict['key'] }} {{ my_dict.key }} |
辞書のキーを使って値にアクセスします。ドット記法も利用可能です。 |
リストの要素へのアクセス | {{ my_list[0] }} |
リストのインデックスを使って要素にアクセスします。 |
オブジェクトの属性へのアクセス | {{ my_object.attribute }} |
オブジェクトの属性にドット記法でアクセスします。 |
コード例
Python コード:
from jinja2 import Template
template = Template("""
名前: {{ name }}
年齢: {{ user.age }}
最初の趣味: {{ hobbies[0] }}
都市: {{ address['city'] }}
""")
data = {
'name': '山田 太郎',
'user': {'age': 30},
'hobbies': ['読書', '映画鑑賞', '旅行'],
'address': {'city': '東京'}
}
output = template.render(data)
print(output)
出力結果:
名前: 山田 太郎
年齢: 30
最初の趣味: 読書
都市: 東京
⚙️ 制御構造 (Control Structures)
条件分岐や繰り返し処理をテンプレート内で実現します。
If 文
構文 | 説明 |
---|---|
{% if condition %} ... {% endif %} |
条件 `condition` が真の場合に内部のコードを実行します。 |
{% if condition %} ... {% else %} ... {% endif %} |
`condition` が真なら最初のブロック、偽なら `else` ブロックを実行します。 |
{% if condition1 %} ... {% elif condition2 %} ... {% else %} ... {% endif %} |
複数の条件分岐を行います。最初に真となった条件のブロックが実行されます。 |
コード例
template = Template("""
{% if user.is_admin %}
管理者です。
{% elif user.is_staff %}
スタッフです。
{% else %}
一般ユーザーです。
{% endif %}
""")
output_admin = template.render(user={'is_admin': True, 'is_staff': False})
output_staff = template.render(user={'is_admin': False, 'is_staff': True})
output_user = template.render(user={'is_admin': False, 'is_staff': False})
print("管理者:", output_admin.strip())
print("スタッフ:", output_staff.strip())
print("一般ユーザー:", output_user.strip())
出力結果:
管理者: 管理者です。
スタッフ: スタッフです。
一般ユーザー: 一般ユーザーです。
For ループ
リストや辞書などのイテラブルオブジェクトを反復処理します。
構文 | 説明 |
---|---|
{% for item in sequence %} ... {% endfor %} |
シーケンス(リスト、タプルなど)の各要素を `item` として反復処理します。 |
{% for key, value in dictionary.items() %} ... {% endfor %} |
辞書のキーと値を反復処理します。 |
{% for item in sequence %} ... {% else %} ... {% endfor %} |
シーケンスが空の場合に `else` ブロックを実行します。 |
{% for i in range(5) %} ... {% endfor %} |
`range` 関数を使って指定回数ループします。 |
{% for item in sequence if condition %} ... {% endfor %} |
ループ内で `condition` が真の要素のみを処理します(フィルタリング)。 |
Loop ヘルパー変数
`for` ループ内では `loop` という特殊な変数にアクセスでき、ループの状態に関する情報を取得できます。
変数 | 説明 |
---|---|
loop.index |
現在の反復回数 (1から始まる)。 |
loop.index0 |
現在の反復回数 (0から始まる)。 |
loop.revindex |
末尾からの反復回数 (1から始まる)。 |
loop.revindex0 |
末尾からの反復回数 (0から始まる)。 |
loop.first |
最初の反復であれば `True`。 |
loop.last |
最後の反復であれば `True`。 |
loop.length |
シーケンスの総要素数。 |
loop.depth |
ネストされたループの現在の深さ (1から始まる)。 |
loop.depth0 |
ネストされたループの現在の深さ (0から始まる)。 |
loop.previtem |
前のループ反復でのアイテム (存在しない場合は `undefined`)。 |
loop.nextitem |
次のループ反復でのアイテム (存在しない場合は `undefined`)。 |
loop.cycle(*args) |
引数で与えられた値を順番に返します。例: `loop.cycle(‘odd’, ‘even’)` は交互に ‘odd’, ‘even’ を返します。 |
コード例
template = Template("""
<ul>
{% for user in users %}
<li class="{{ loop.cycle('odd', 'even') }}{% if loop.first %} first{% endif %}{% if loop.last %} last{% endif %}">
{{ loop.index }}: {{ user.name }} {% if user.age > 30 %}(ベテラン){% endif %}
</li>
{% else %}
<li>ユーザーはいません。</li>
{% endfor %}
</ul>
<dl>
{% for key, value in config.items() %}
<dt>{{ key }}</dt>
<dd>{{ value }}</dd>
{% endfor %}
</dl>
Filtered List:
{% for x in [1, 2, 3, 4, 5] if x is even %}
- {{ x }}
{% endfor %}
""")
data = {
'users': [
{'name': 'Alice', 'age': 25},
{'name': 'Bob', 'age': 35},
{'name': 'Charlie', 'age': 28}
],
'config': {'theme': 'dark', 'debug': True}
}
output = template.render(data)
print(output)
# 空のリストの場合
empty_data = {'users': [], 'config': {}}
output_empty = template.render(empty_data)
print("\n--- 空の場合 ---")
print(output_empty)
出力結果:
<ul>
<li class="odd first">
1: Alice
</li>
<li class="even">
2: Bob (ベテラン)
</li>
<li class="odd last">
3: Charlie
</li>
</ul>
<dl>
<dt>theme</dt>
<dd>dark</dd>
<dt>debug</dt>
<dd>True</dd>
</dl>
Filtered List:
- 2
- 4
--- 空の場合 ---
<ul>
<li>ユーザーはいません。</li>
</ul>
<dl>
</dl>
Filtered List:
💧 フィルター (Filters)
変数の表示形式を変更したり、データを加工したりします。パイプ (`|`) 記号を使って適用します。
フィルター名 | 例 | 説明 |
---|---|---|
文字列操作 | ||
capitalize |
{{ "hello world" | capitalize }} |
最初の文字を大文字にし、残りを小文字にします (例: “Hello world”)。 |
lower |
{{ "HELLO" | lower }} |
文字列全体を小文字にします (例: “hello”)。 |
upper |
{{ "hello" | upper }} |
文字列全体を大文字にします (例: “HELLO”)。 |
title |
{{ "hello world" | title }} |
各単語の最初の文字を大文字にします (例: “Hello World”)。 |
trim |
{{ " hello " | trim }} |
文字列の前後の空白を除去します (例: “hello”)。 |
striptags |
{{ "<p>Hello</p>" | striptags }} |
文字列からHTML/XMLタグを除去します (例: “Hello”)。 |
replace |
{{ "Hello World" | replace("World", "Jinja") }} |
文字列の一部を置換します (例: “Hello Jinja”)。 |
length / count |
{{ "abc" | length }} {{ [1, 2, 3] | length }} |
文字列の文字数やシーケンスの要素数を返します (例: 3)。 |
wordcount |
{{ "Hello world example" | wordcount }} |
文字列中の単語数を返します (例: 3)。 |
数値操作 | ||
abs |
{{ -5 | abs }} |
数値の絶対値を返します (例: 5)。 |
round |
{{ 42.55 | round }} {{ 42.55 | round(1, 'floor') }} |
数値を四捨五入します。引数で小数点以下の桁数や丸め方(`ceil`, `floor`)を指定できます (例: 43.0, 42.5)。 |
int |
{{ "42" | int }} {{ 3.14 | int }} |
値を整数に変換します (例: 42, 3)。変換できない場合はデフォルト値(指定可能、デフォルト0)を返します。 |
float |
{{ "3.14" | float }} {{ 42 | float }} |
値を浮動小数点数に変換します (例: 3.14, 42.0)。変換できない場合はデフォルト値(指定可能、デフォルト0.0)を返します。 |
リスト/辞書操作 | ||
first |
{{ [1, 2, 3] | first }} |
シーケンスの最初の要素を返します (例: 1)。 |
last |
{{ [1, 2, 3] | last }} |
シーケンスの最後の要素を返します (例: 3)。 |
join |
{{ ["a", "b", "c"] | join(", ") }} |
シーケンスの要素を指定した区切り文字で連結した文字列を返します (例: “a, b, c”)。 |
sort |
{{ [3, 1, 2] | sort }} {{ users | sort(attribute='age') }} |
シーケンスをソートします。`reverse=True`で降順、`attribute`でオブジェクトの属性を指定してソート可能 (例: `[1, 2, 3]`)。 |
reverse |
{{ [1, 2, 3] | reverse }} {{ "abc" | reverse }} |
シーケンスや文字列を逆順にします (例: `[3, 2, 1]`, `”cba”`)。ジェネレータを返すため、`list`フィルター等と組み合わせることが多いです。 |
map |
{{ users | map(attribute='name') | list }} |
シーケンスの各要素に関数や属性アクセスを適用します (例: `[‘Alice’, ‘Bob’]`)。`list`フィルター等が必要です。 |
select |
{{ users | selectattr('is_active') | list }} {{ numbers | select('greaterthan', 5) | list }} |
シーケンスの要素をテスト(関数、テスト名、属性)でフィルタリングします (例: アクティブなユーザーのみのリスト)。`list`フィルター等が必要です。 |
reject |
{{ users | rejectattr('is_admin') | list }} {{ numbers | reject('odd') | list }} |
`select`の逆で、テストが偽となる要素のみを選択します (例: 管理者でないユーザーのみのリスト)。`list`フィルター等が必要です。 |
sum |
{{ [1, 2, 3] | sum }} {{ users | sum(attribute='points') }} |
シーケンス内の数値の合計を返します。`attribute` でオブジェクトの属性を指定できます (例: 6)。 |
list |
{{ "abc" | list }} {{ range(3) | list }} |
イテラブルオブジェクトをリストに変換します (例: `[‘a’, ‘b’, ‘c’]`, `[0, 1, 2]`)。`map`, `select`, `reject`, `reverse` などの結果をリスト化するのに使います。 |
HTML/URL | ||
escape / e |
{{ "<script>" | escape }} {{ user_input | e }} |
HTML特殊文字 (`<`, `>`, `&`, `”`, `’`) をエスケープします。XSS対策に重要です。e は短縮形。 |
urlencode |
{{ "a=1&b= hello" | urlencode }} |
文字列をURLエンコードします (例: “a%3D1%26b%3D%20hello”)。 |
その他 | ||
default / d |
{{ undefined_var | default("N/A") }} {{ age | d(0) }} |
変数が未定義または偽 (オプションで変更可) の場合にデフォルト値を返します。d は短縮形。 |
batch |
{{ items | batch(3, ' ') }} |
シーケンスを指定したサイズのリストのリストに分割します。第2引数で要素数が足りない場合の埋め草を指定できます。 |
slice |
{{ items | slice(3) }} {{ items | slice(3, ',') }} |
シーケンスを指定した数の行にスライスします。各行はリストになります。第2引数で埋め草を指定できます。 |
safe |
{{ html_content | safe }} |
変数を安全(エスケープ不要)とマークします。自動エスケープが有効な環境でHTMLをそのまま出力したい場合に使用します。信頼できない入力には絶対に使用しないでください。 |
pprint |
{{ my_complex_object | pprint }} |
Pythonの `pprint` モジュールを使って、変数を整形して表示します。デバッグに便利です。 |
xmlattr |
{{ {'class': 'foo', 'alt': 'bar', 'other': none} | xmlattr }} |
辞書からXML/HTML属性文字列を生成します。値が `None` や `False` のキーは無視されます (例: ` class=”foo” alt=”bar”`)。 |
コード例
template = Template("""
名前 (大文字): {{ name | upper }}
挨拶 (デフォルト値): {{ greeting | default("こんにちは") }}
最初のタグ: {{ tags | first | capitalize }}
タグリスト: {{ tags | join(", ") }}
数値リスト (バッチ処理 2個ずつ):
{% for row in numbers | batch(2, '-') %}
{{ row | join(" | ") }}
{% endfor %}
HTMLコンテンツ: {{ html_code | e }}
安全なHTML: {{ safe_html | safe }}
""")
data = {
'name': 'Sato',
'tags': ['python', 'jinja', 'web'],
'numbers': [1, 2, 3, 4, 5],
'html_code': '',
'safe_html': 'This is safe.
'
# 'greeting' は未定義
}
output = template.render(data)
print(output)
出力結果:
名前 (大文字): SATO
挨拶 (デフォルト値): こんにちは
最初のタグ: Python
タグリスト: python, jinja, web
数値リスト (バッチ処理 2個ずつ):
1 | 2
3 | 4
5 | -
HTMLコンテンツ: <button onclick="alert('XSS')">Click me</button>
安全なHTML: <p style="color: green;">This is safe.</p>
✅ テスト (Tests)
変数の型や値をチェックし、`if` 文などの条件式で使用します。`is` キーワードを使って適用します。
テスト名 | 例 | 説明 |
---|---|---|
型のテスト | ||
defined |
{% if my_var is defined %} ... {% endif %} |
変数が定義されていれば `True`。 |
undefined |
{% if my_var is undefined %} ... {% endif %} |
変数が未定義であれば `True`。 |
none |
{% if my_var is none %} ... {% endif %} |
変数が `None` であれば `True`。 |
boolean |
{% if my_var is boolean %} ... {% endif %} |
変数が真偽値 (`True` または `False`) であれば `True`。 |
callable |
{% if my_func is callable %} ... {% endif %} |
変数が呼び出し可能(関数やメソッドなど)であれば `True`。 |
even |
{% if count is even %} ... {% endif %} |
数値が偶数であれば `True`。 |
odd |
{% if count is odd %} ... {% endif %} |
数値が奇数であれば `True`。 |
iterable |
{% if my_list is iterable %} ... {% endif %} |
変数がイテラブル(リスト、タプル、文字列など)であれば `True`。 |
mapping |
{% if my_dict is mapping %} ... {% endif %} |
変数がマッピング(辞書など)であれば `True`。 |
number |
{% if price is number %} ... {% endif %} |
変数が数値(整数、浮動小数点数など)であれば `True`。 |
sequence |
{% if my_tuple is sequence %} ... {% endif %} |
変数がシーケンス(リスト、タプル、文字列など。マッピングを除く)であれば `True`。 |
string |
{% if name is string %} ... {% endif %} |
変数が文字列であれば `True`。 |
sameas |
{% if var1 is sameas var2 %} ... {% endif %} |
2つの変数がPythonの `is` 演算子で比較して同じオブジェクトであれば `True`。 |
値の比較 | ||
eq / == |
{% if score is eq 100 %} ... {% endif %} {% if score == 100 %} ... {% endif %} |
値が等しければ `True`。 |
ne / != |
{% if status is ne 'pending' %} ... {% endif %} {% if status != 'pending' %} ... {% endif %} |
値が等しくなければ `True`。 |
lt / < |
{% if age is lt 18 %} ... {% endif %} {% if age < 18 %} ... {% endif %} |
値がより小さければ `True`。 |
le / <= |
{% if count is le 10 %} ... {% endif %} {% if count <= 10 %} ... {% endif %} |
値が以下であれば `True`。 |
gt / > |
{% if temperature is gt 30 %} ... {% endif %} {% if temperature > 30 %} ... {% endif %} |
値がより大きければ `True`。 |
ge / >= |
{% if progress is ge 100 %} ... {% endif %} {% if progress >= 100 %} ... {% endif %} |
値が以上であれば `True`。 |
包含テスト | ||
in |
{% if 'admin' in user.roles %} ... {% endif %} {% if 'apple' in fruits %} ... {% endif %} |
左辺の値が右辺のコンテナ(リスト、タプル、辞書、文字列など)に含まれていれば `True`。 |
コード例
template = Template("""
{% if user is defined and user is mapping %}
ユーザー名: {{ user.name | default('不明') }}
{% if user.age is defined and user.age is number and user.age >= 18 %}
年齢は {{ user.age }} 歳で、成人です。
{% if user.age is odd %} 年齢は奇数です。 {% endif %}
{% elif user.age is defined %}
年齢は {{ user.age }} 歳で、未成年です。
{% else %}
年齢は不明です。
{% endif %}
{% if 'admin' in user.roles %}
管理者権限を持っています。
{% endif %}
{% else %}
ユーザー情報がありません。
{% endif %}
{% if value is none %}
値はNoneです。
{% endif %}
{% if count == 5 %}
カウントは5です。
{% endif %}
""")
data1 = {
'user': {
'name': 'Alice',
'age': 25,
'roles': ['editor', 'user']
},
'value': None,
'count': 5
}
data2 = {
'user': {
'age': 15,
'roles': ['admin', 'user']
}
# 'name' は未定義
}
data3 = {}
output1 = template.render(data1)
output2 = template.render(data2)
output3 = template.render(data3)
print("--- Data 1 ---")
print(output1)
print("--- Data 2 ---")
print(output2)
print("--- Data 3 ---")
print(output3)
出力結果:
--- Data 1 ---
ユーザー名: Alice
年齢は 25 歳で、成人です。
年齢は奇数です。
値はNoneです。
カウントは5です。
--- Data 2 ---
ユーザー名: 不明
年齢は 15 歳で、未成年です。
管理者権限を持っています。
--- Data 3 ---
ユーザー情報がありません。
🧩 マクロ (Macros)
テンプレート内で再利用可能な関数のようなものを定義します。HTMLの断片やロジックをカプセル化するのに便利です。
定義と呼び出し
{% macro input(name, value='', type='text', size=20) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}">
{% endmacro %}
{{ input('username') }}
{{ input('password', type='password') }}
{{ input('email', 'test@example.com', size=30) }}
`macro` タグでマクロを定義し、通常の変数のように呼び出します。引数を渡すことができ、デフォルト値を設定することも可能です。
`caller()`
`call` タグを使ってマクロを呼び出す際に、呼び出し元のブロックの内容をマクロ内で利用できます。
{% macro render_dialog(title, class='dialog') %}
<div class="{{ class }}">
<h2>{{ title }}</h2>
<div class="content">
{{ caller() }}
</div>
</div>
{% endmacro %}
{% call render_dialog('ようこそ') %}
これはダイアログの本文です。<br>
HTMLも利用可能です。
{% endcall %}
{% call(user) render_dialog(user.name, class='user-profile') %}
年齢: {{ user.age }}<br>
メール: {{ user.email }}
{% endcall %}
`caller()` は `call` ブロック内のレンダリング結果を返します。`call` タグに引数を渡すと、`caller` にもその引数が渡されます。
インポート
他のテンプレートファイルで定義されたマクロを利用するには、`import` または `from … import` を使います。
`import`
テンプレート全体をインポートし、`.` (ドット) を使ってマクロにアクセスします。
例: `_forms.html` に `input` マクロが定義されている場合
{% import '_forms.html' as forms %}
{{ forms.input('username') }}
{{ forms.input('password', type='password') }}
`from … import`
特定の名前のマクロを現在のテンプレートのスコープに直接インポートします。
{% from '_forms.html' import input as input_field, textarea %}
{{ input_field('email') }}
{{ textarea('message') }}
`with context` を付けると、インポート元のコンテキスト(変数)がインポートしたマクロ内で利用可能になります。`without context` を付けると利用できなくなります(デフォルト)。
{% from '_helpers.html' import my_macro with context %}
{% from '_utils.html' import utility_macro without context %}
{{ my_macro() }} {# 現在のコンテキストが利用可能 #}
{{ utility_macro() }} {# 現在のコンテキストは利用不可 #}
🏗️ テンプレート継承 (Template Inheritance)
テンプレートの骨組み(レイアウト)を定義し、子テンプレートで特定の部分を上書きする仕組みです。コードの再利用性を高めます。
タグ/関数 | 構文 | 説明 |
---|---|---|
extends |
{% extends "base_layout.html" %} |
このテンプレートが継承する親テンプレートを指定します。通常、テンプレートの最初に記述します。 |
block |
{% block block_name %} ... {% endblock %} |
親テンプレートで定義されたブロックを上書きします。親テンプレートにも同じ名前の `block` タグが必要です。 |
super() |
{{ super() }} |
`block` 内で使用し、親テンプレートの同じ名前のブロックの内容をレンダリングします。 |
コード例
親テンプレート (`base.html`):
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}デフォルトタイトル{% endblock %}</title>
{% block head %}
<link rel="stylesheet" href="style.css">
{% endblock %}
</head>
<body>
<div id="content">
{% block content %}{% endblock %}
</div>
<div id="footer">
{% block footer %}
© 2025 My Company
{% endblock %}
</div>
</body>
</html>
子テンプレート (`child.html`):
{% extends "base.html" %}
{% block title %}マイページ - {{ super() }}{% endblock %}
{% block head %}
{{ super() }}
<!-- このページ固有のスタイルシート -->
<link rel="stylesheet" href="mypage.css">
{% endblock %}
{% block content %}
<h1>ようこそ、{{ username }} さん!</h1>
<p>これはマイページのコンテンツです。</p>
{% endblock %}
{# footerブロックは上書きしないので、親テンプレートの内容が使われる #}
`child.html` を `username=’ゲスト’` でレンダリングすると、`base.html` の構造に `child.html` のブロック内容が挿入されたHTMLが出力されます。`title` と `head` ブロックでは `super()` を使って親の内容を含めています。
📥 インクルード (Include)
他のテンプレートファイルの内容を現在のテンプレートに挿入します。ヘッダー、フッター、サイドバーなど、共通部分のパーツを読み込むのに便利です。
構文 | 説明 |
---|---|
{% include "header.html" %} |
`header.html` の内容をレンダリングして挿入します。現在のコンテキスト(変数)が `header.html` に引き継がれます。 |
{% include "sidebar.html" with context %} |
明示的に現在のコンテキストを引き継ぎます(デフォルトの動作と同じ)。 |
{% include "ad_banner.html" without context %} |
現在のコンテキストを引き継がずに `ad_banner.html` をレンダリングします。インクルードされるテンプレートが外部の変数に依存しないようにする場合に便利です。 |
{% include "user_widget.html" with {'user': current_user, 'show_details': true} %} |
指定した変数のみを `user_widget.html` に渡してレンダリングします。 |
{% include ["sidebar_desktop.html", "sidebar_mobile.html"] %} |
リストで複数のテンプレート名を指定できます。最初に見つかったテンプレートがインクルードされます。 |
{% include "optional_info.html" ignore missing %} |
`ignore missing` を付けると、指定されたテンプレートが存在しない場合でもエラーにならず、何も出力しません。 |
コード例
`main.html`:
<div class="page">
{% include "header.html" %}
<div class="content">
<h1>{{ page_title }}</h1>
<p>メインコンテンツエリア</p>
{# user変数だけを渡す #}
{% include "user_profile.html" with {'user': current_user} %}
</div>
{# コンテキストを渡さない #}
{% include "footer.html" without context ignore missing %}
</div>
`header.html`:
<header>
サイトロゴ - {{ site_name | default('My Site') }}
{% if current_user %}
<span>ようこそ、{{ current_user.name }}さん</span>
{% endif %}
</header>
`user_profile.html`:
<div class="profile">
<h2>{{ user.name }}のプロフィール</h2>
<p>メール: {{ user.email }}</p>
{# page_title は渡されていないので使えない #}
{# <p>現在のページ: {{ page_title }}</p> これはエラーになるか空になる #}
</div>
`main.html` をレンダリングする際に `page_title`, `site_name`, `current_user` などの変数を渡すと、`header.html` では `current_user` や `site_name` が利用でき、`user_profile.html` では渡された `user` のみが利用できます。`footer.html` は存在すればコンテキストなしで読み込まれます。
📝 コメント (Comments)
テンプレート内にメモを残しますが、レンダリング結果には出力されません。
構文 | 説明 |
---|---|
{# これはコメントです #} |
一行または複数行のコメントを記述します。この部分は出力に含まれません。 |
コード例
{# ユーザー情報を表示するセクション #}
<div class="user-info">
{# 名前を表示 #}
<p>名前: {{ user.name }}</p>
{#
複数行のコメントも可能です。
年齢はオプションで表示します。
#}
{% if user.age %}
<p>年齢: {{ user.age }}</p> {# 年齢を表示 #}
{% endif %}
</div>
{# --- ここまでユーザー情報 --- #}
🌬️ 空白制御 (Whitespace Control)
テンプレートタグの前後にある空白(改行含む)を制御します。タグの開始/終了デリミタにハイフン (`-`) を追加します。
構文 | 説明 |
---|---|
{%- ... %} |
タグの直前の空白(改行含む)を削除します。 |
{% ... -%} |
タグの直後の空白(改行含む)を削除します。 |
{{- ... }} |
変数タグの直前の空白を削除します。 |
{{ ... -}} |
変数タグの直後の空白を削除します。 |
{#- ... #} |
コメントタグの直前の空白を削除します。 |
{# ... -#} |
コメントタグの直後の空白を削除します。 |
コード例
制御なし:
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
出力 (改行とインデントがそのまま残る):
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Cherry</li>
</ul>
空白制御あり:
<ul>
{%- for item in items -%}
<li>{{- item -}}</li>
{%- endfor -%}
</ul>
出力 (タグ周りの不要な空白が削除される):
<ul><li>Apple</li><li>Banana</li><li>Cherry</li></ul>
部分的な制御:
<ul>
{% for item in items %}
<li>{{ item }}</li>{% endfor %}
</ul>
出力 (endforの後ろの改行のみ削除):
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Cherry</li></ul>
環境設定で `trim_blocks` や `lstrip_blocks` を有効にすると、ブロックタグ周りの空白を自動的に制御することも可能です。
🛡️ エスケープ (Escaping)
セキュリティ(特にクロスサイトスクリプティング – XSS)のために、テンプレートに変数を埋め込む際のHTML特殊文字の扱いを制御します。
自動エスケープ
Jinja2 環境の多く(Flask, Djangoなど)では、デフォルトで自動エスケープが有効になっています。これにより、`{{ variable }}` で出力される変数は自動的にHTMLエスケープされます。
# 環境で autoescape=True (デフォルトが多い)
template = Template("Hello, {{ name }}")
output = template.render(name='<b>World</b>')
print(output) # 出力: Hello, <b>World</b>
手動エスケープ
自動エスケープが無効な環境や、明示的にエスケープしたい場合に `escape` または `e` フィルターを使用します。
# 環境で autoescape=False
template = Template("Data: {{ user_input | e }}")
output = template.render(user_input='<script>alert("XSS")</script>')
print(output) # 出力: Data: <script>alert("XSS")</script>
エスケープの無効化
変数の内容が安全なHTMLであり、エスケープせずにそのまま出力したい場合は、`safe` フィルターを使用するか、変数を `Markup` オブジェクトにします。
警告: ユーザー入力など、信頼できないソースからのデータを `safe` にマークするのは非常に危険です。XSS脆弱性の原因となります。
from markupsafe import Markup
# 自動エスケープが有効な環境
template = Template("""
Escaped: {{ unsafe_html }}
Safe: {{ safe_html | safe }}
Markup Object: {{ markup_obj }}
""")
data = {
'unsafe_html': '<strong>Unsafe</strong>',
'safe_html': '<em>This is marked safe</em>', # 本来は信頼できるソースから生成されたHTML
'markup_obj': Markup('<a href="#">Link</a>')
}
output = template.render(data)
print(output)
出力結果:
Escaped: <strong>Unsafe</strong>
Safe: <em>This is marked safe</em>
Markup Object: <a href="#">Link</a>
`autoescape` ブロック
テンプレートの一部で自動エスケープの挙動を切り替えることができます。
{% autoescape true %}
{{ var1 }} {# エスケープされる #}
{% autoescape false %}
{{ var2 }} {# エスケープされない #}
{% endautoescape %}
{{ var3 }} {# 再びエスケープされる #}
{% endautoescape %}
{# XMLを生成する場合など #}
{% autoescape 'xml' %}
<foo>{{ data }}</foo> {# XMLエスケープ #}
{% endautoescape %}
`true`, `false` の他に、ファイル拡張子に応じたエスケープ(例: `’html’`, `’xml’`)を指定することも可能です。
✨ 特殊変数 (Special Variables)
Jinja2が提供する、特定のコンテキストで利用可能な特殊な変数です。
変数名 | 利用可能な場所 | 説明 |
---|---|---|
loop |
`for` ループ内 | ループの状態に関する情報(インデックス、最初/最後かなど)を提供します。(詳細は制御構造のセクションを参照) |
self |
マクロ内 | 現在のマクロ自身を参照します。再帰的なマクロ呼び出しなどに使用できます。 例: {{ self(level - 1) }} |
varargs |
マクロ内 | マクロ呼び出し時に渡された、名前のない余分な引数をリストとして受け取ります。{% macro mymacro(arg1, *varargs) %}...{% endmacro %} |
kwargs |
マクロ内 | マクロ呼び出し時に渡された、定義されていないキーワード引数を辞書として受け取ります。{% macro mymacro(arg1, **kwargs) %}...{% endmacro %} |
namespace |
どこでも (要定義) | ループやマクロなどの内部スコープから外部スコープの変数を変更するためのオブジェクト。明示的に作成して利用します。{% set ns = namespace(counter=0) %} {% for item in items %}{% set ns.counter = ns.counter + 1 %}{% endfor %} Total: {{ ns.counter }} |
`namespace` のコード例
template = Template("""
{% set ns = namespace(found=false, count=0) %}
{% for item in items %}
{% if item.startswith('A') %}
{% set ns.found = true %}
{% set ns.count = ns.count + 1 %}
- {{ item }} (Found!)
{% else %}
- {{ item }}
{% endif %}
{% endfor %}
{% if ns.found %}
Found {{ ns.count }} items starting with 'A'.
{% else %}
No items starting with 'A' found.
{% endif %}
""")
data = {'items': ['Apple', 'Banana', 'Avocado', 'Cherry', 'Apricot']}
output = template.render(data)
print(output)
data_none = {'items': ['Banana', 'Cherry']}
output_none = template.render(data_none)
print("\n--- None Found ---")
print(output_none)
出力結果:
- Apple (Found!)
- Banana
- Avocado (Found!)
- Cherry
- Apricot (Found!)
Found 3 items starting with 'A'.
--- None Found ---
- Banana
- Cherry
No items starting with 'A' found.
`namespace` を使わないと、`for` ループ内で `found` や `count` を変更しても、ループの外側には反映されません。
🔧 環境設定と拡張 (Environment & Extensions)
Jinja2の動作をカスタマイズするための主要な概念です。チートシートの範囲を超えますが、基本的な点をいくつか紹介します。
Environment
Jinja2の中核となるオブジェクトです。テンプレートのロード方法、構文デリミタ、自動エスケープの有無、拡張機能などを設定します。
from jinja2 import Environment, FileSystemLoader
# ファイルシステムからテンプレートをロードする環境を作成
env = Environment(
loader=FileSystemLoader('templates/'), # テンプレートを探すディレクトリ
autoescape=True, # 自動HTMLエスケープを有効化
trim_blocks=True, # ブロックタグの後の改行を削除
lstrip_blocks=True # ブロックタグの前のインデントを削除
)
# 拡張機能を追加
# env.add_extension('jinja2.ext.do')
# テンプレートをロードしてレンダリング
template = env.get_template('mytemplate.html')
output = template.render(my_variable='value')
ローダー (Loaders)
テンプレートファイルをどこから読み込むかを定義します。
FileSystemLoader(searchpath)
: 指定されたディレクトリからテンプレートを読み込みます。PackageLoader(package_name, package_path='templates')
: Pythonパッケージ内からテンプレートを読み込みます。DictLoader(mapping)
: Pythonの辞書からテンプレートを読み込みます。ChoiceLoader(loaders)
: 複数のローダーを試し、最初に見つかったものを使用します。PrefixLoader(mapping, delimiter='/')
: プレフィックスに応じて異なるローダーに委譲します。
拡張機能 (Extensions)
Jinja2のコア機能を拡張します。`Environment` 作成時に `extensions` リストで指定するか、`add_extension` メソッドで追加します。
- `jinja2.ext.do`: テンプレート内で式を実行できる `{% do %}` タグを追加します。例: `{% do mylist.append(1) %}`
- `jinja2.ext.loopcontrols`: `for` ループ内で `break` と `continue` を使えるようにします。
`{% for item in items %}{% if item.invalid %}{% continue %}{% endif %}{% if item.is_last %}{% break %}{% endif %}…{% endfor %}` - `jinja2.ext.with_`: より複雑な変数のスコープ管理を行う `{% with %}` タグを追加します(Jinja2 3.0以降はコア機能)。
`{% with foo=42, bar=user.name %}{{ foo }} – {{ bar }}{% endwith %}` - `jinja2.ext.autoescape`: `{% autoescape %}` ブロックを提供します(Jinja2 2.9以降はコア機能)。
- `jinja2.ext.i18n`: 国際化(i18n)サポートを追加し、`{% trans %}` タグなどを提供します。
`do` 拡張のコード例
from jinja2 import Environment
env = Environment(extensions=['jinja2.ext.do'])
template = env.from_string("""
{% set mylist = [] %}
{% for i in range(5) %}
{% do mylist.append(i * 2) %}
{% endfor %}
List: {{ mylist | join(', ') }}
""")
output = template.render()
print(output) # 出力: List: 0, 2, 4, 6, 8
コメント