Jinja2 チートシート

テンプレート内で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
最初の趣味: 読書
都市: 東京

条件分岐や繰り返し処理をテンプレート内で実現します。

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:

変数の表示形式を変更したり、データを加工したりします。パイプ (`|`) 記号を使って適用します。

フィルター名説明
文字列操作
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>

変数の型や値をチェックし、`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 --- ユーザー情報がありません。

テンプレート内で再利用可能な関数のようなものを定義します。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() }} {# 現在のコンテキストは利用不可 #}

テンプレートの骨組み(レイアウト)を定義し、子テンプレートで特定の部分を上書きする仕組みです。コードの再利用性を高めます。

タグ/関数構文説明
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 "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` は存在すればコンテキストなしで読み込まれます。

テンプレート内にメモを残しますが、レンダリング結果には出力されません。

構文説明
{# これはコメントです #}一行または複数行のコメントを記述します。この部分は出力に含まれません。

コード例

{# ユーザー情報を表示するセクション #}
<div class="user-info"> {# 名前を表示 #} <p>名前: {{ user.name }}</p> {# 複数行のコメントも可能です。 年齢はオプションで表示します。 #} {% if user.age %} <p>年齢: {{ user.age }}</p> {# 年齢を表示 #} {% endif %}
</div>
{# --- ここまでユーザー情報 --- #}

テンプレートタグの前後にある空白(改行含む)を制御します。タグの開始/終了デリミタにハイフン (`-`) を追加します。

構文説明
{%- ... %}タグの直前の空白(改行含む)を削除します。
{% ... -%}タグの直後の空白(改行含む)を削除します。
{{- ... }}変数タグの直前の空白を削除します。
{{ ... -}}変数タグの直後の空白を削除します。
{#- ... #}コメントタグの直前の空白を削除します。
{# ... -#}コメントタグの直後の空白を削除します。

コード例

制御なし:

<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` を有効にすると、ブロックタグ周りの空白を自動的に制御することも可能です。

セキュリティ(特にクロスサイトスクリプティング – XSS)のために、テンプレートに変数を埋め込む際のHTML特殊文字の扱いを制御します。

自動エスケープ

Jinja2 環境の多く(Flask, Djangoなど)では、デフォルトで自動エスケープが有効になっています。これにより、`{{ variable }}` で出力される変数は自動的にHTMLエスケープされます。

# 環境で autoescape=True (デフォルトが多い)
template = Template("Hello, {{ name }}")
output = template.render(name='<b>World</b>')
print(output) # 出力: Hello, &lt;b&gt;World&lt;/b&gt;

手動エスケープ

自動エスケープが無効な環境や、明示的にエスケープしたい場合に `escape` または `e` フィルターを使用します。

# 環境で autoescape=False
template = Template("Data: {{ user_input | e }}")
output = template.render(user_input='<script>alert("XSS")</script>')
print(output) # 出力: Data: &lt;script&gt;alert("XSS")&lt;/script&gt;

エスケープの無効化

変数の内容が安全な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: &lt;strong&gt;Unsafe&lt;/strong&gt;
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’`)を指定することも可能です。

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` を変更しても、ループの外側には反映されません。

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

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です