Jinja2 チートシート

cheatsheet

テンプレート内で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

コメント

タイトルとURLをコピーしました