Traffine I/O

日本語

2023-03-10

PythonにおけるJinjaテンプレートエンジン

Jinja とは

Jinjaは、Pythonで動的なWebページ、HTML、XML、その他のマークアップ言語を生成するために広く使用される、人気のあるテンプレートエンジンです。

Djangoのテンプレートシステムに触発され、プレゼンテーション層とビジネスロジック層を効率的に分離する方法を提供します。テンプレートの継承、マクロ、フィルター、ループ制御などの強力な機能により、Jinjaを使用することで、開発者は簡単に複雑で動的なテンプレートを作成することができます。

Jinja の使い方

PythonでJinjaを設定するには、Jinjaパッケージをインストールしてテンプレートファイルを作成するというプロセスが必要です。以下は、PythonでJinjaを設定する方法の例です。

  1. pipを使用してJinjaパッケージをインストールします。
bash
$ pip install jinja2
  1. 次の内容を持つtemplate.htmlという名前のテンプレートファイルを作成します。
template.html
<!DOCTYPE html>
<html>
<head>
	<title>{{ title }}</title>
</head>
<body>
	<h1>{{ heading }}</h1>
	<ul>
	{% for item in items %}
		<li>{{ item }}</li>
	{% endfor %}
	</ul>
</body>
</html>
  1. Pythonのスクリプトで、jinja2モジュールをインポートし、テンプレートファイルを読み込みます。
python
from jinja2 import Template, FileSystemLoader, Environment

env = Environment(loader=FileSystemLoader('.'))
template = env.get_template('template.html')
  1. テンプレートで使用する変数を定義します。
python
title = 'My Template'
heading = 'Welcome to my template'
items = ['item 1', 'item 2', 'item 3']
  1. 定義した変数を使用してテンプレートをレンダリングします。
python
output = template.render(title=title, heading=heading, items=items)
print(output)

これにより、定義した変数がテンプレートに挿入されたレンダリングされたHTMLコードが出力されます。出力をファイルに書き込むこともでき、Webフレームワークに渡してWebページに表示することもできます。

<!DOCTYPE html>
<html>
  <head>
    <title>My Template</title>
  </head>
  <body>
    <h1>Welcome to my template</h1>
    <ul>
      <li>item 1</li>
      <li>item 2</li>
      <li>item 3</li>
    </ul>
  </body>
</html>

Jinja の構文

Jinjaの利点の1つは、読みやすく理解しやすいテンプレートを作成できる簡単な構文です。

変数

Jinjaのもっとも一般的な機能の1つは、変数を使用してテンプレートに動的データを挿入できることです。変数はダブルカーリーブラケット{{ variable_name }}を使用して定義されます。例えば、nameという値がJohnの変数がある場合、次のようにテンプレートに挿入できます。

html
<p>My name is {{ name }}.</p>

ループ

Jinjaのもう1つの強力な機能は、リストや辞書などのデータ構造をループ処理することができることです。これは、繰り返しマークアップを生成する必要がある場合や、データを反復処理する必要がある場合に便利です。ループの構文は{% for item in list %} ... {% endfor %}です。例えば、名前のリストがある場合、次のようにループ処理できます。

html
<ul>
  {% for name in names %}
    <li>{{ name }}</li>
  {% endfor %}
</ul>

ループ固有の変数

Jinjaでは、ループを使用してアイテムのコレクションを反復処理し、各アイテムにアクションを実行することができます。反復処理中、Jinjaはループをより制御するための特定の変数を提供し、反復処理に関する特定の情報にアクセスできるようにします。Jinjaでのループ固有の変数のリストは次のとおりです。

変数 説明
loop.index この変数は、現在の反復インデックスを表します。1 から開始します。
loop.index0 この変数は、現在の反復インデックスを表します。0 から開始します
loop.revindex この変数は、末尾からの反復回数を表します。1 から開始します。
loop.revindex0 この変数は、末尾からの反復回数を表します。0 から開始します。
loop.first この変数は、最初の反復処理中に True になり、それ以外の場合には False になります。
loop.last この変数は、最後の反復処理中に True になり、それ以外の場合には False になります。
loop.length この変数は、反復の合計回数を表します。

以下は、これらの変数をJinjaで使用する例です。

html
<ul>
{% for item in items %}
    <li>{{ loop.index }}. {{ item }}</li>
{% endfor %}
</ul>

この例では、itemsのコレクションを反復処理し、各アイテムを順序なしリスト(<ul>)に表示しています。 loop.index変数を使用して、各アイテムに反復インデックスを表示しています。

Jinjaでは、dictsortフィルタを使用して辞書のリストを反復することもできます。次の例は、これを示しています。

html
<ul>
{% for item in items|dictsort(attribute='name') %}
    <li>{{ item.name }} ({{ item.price }})</li>
{% endfor %}
</ul>

この例では、namepriceキーを含む辞書のリストを反復処理しています。反復する前に、dictsortフィルタを使用してリストをname属性でソートしています。ループ内では、各辞書の値にアクセスするために、item.nameおよびitem.priceの構文を使用しています。

条件文

Jinjaは条件文をサポートしており、特定の条件に基づいてマークアップを表示または非表示にすることができます。条件文の構文は{% if condition %} ... {% endif %}です。例えば、is_logged_inという変数がユーザーがログインしているかどうかを示す場合、その値に基づいて異なるコンテンツを表示できます。

html
{% if is_logged_in %}
  <p>Welcome back!</p>
{% else %}
  <p>Please log in to continue.</p>
{% endif %}

Jinja の高度な機能

基本的な構文機能に加えて、Jinjaにはマクロやテンプレート継承などの高度な機能があり、テンプレートの柔軟性と効率性をさらに向上させることができます。

マクロ

その高度な機能の1つは、再利用可能なコードスニペット(マクロと呼ばれる)を定義できることです。マクロはプログラミング言語の関数に似ており、共通に使用されるコードをカプセル化してテンプレートを簡素化、整理することができます。

Jinjaでマクロを定義するには、{% macro %}タグを使用します。例を示します。

{% macro greeting(name) %}
    Hello, {{ name }}!
{% endmacro %}

このマクロは、greetingという名前で、1つの引数nameを受け取ります。{{ name }}構文を使用してname変数の値を出力する簡単な挨拶メッセージを返します。

テンプレートでgreetingマクロを使用するには、{% call %}タグを使って呼び出します。

{% call greeting('Alice') %}
{% endcall %}

これにより、次の出力がレンダリングされます。

Hello, Alice!

変数をマクロに渡すこともできます。

{% set my_name = 'Bob' %}
{% call greeting(my_name) %}
{% endcall %}

これにより、次の出力がレンダリングされます。

Hello, Bob!

Jinjaでマクロを定義することで、テンプレートで同じコードを繰り返すことを避け、よりモジュラーでメンテナンスしやすいテンプレートを作成できます。

テンプレートの継承

テンプレートの継承は、Jinjaの強力な機能の1つで、1つのテンプレートの共通要素を複数のページで再利用することができます。テンプレートの継承を使用すると、ヘッダーやフッター、ナビゲーションメニューなど、サイトの共通要素を含むベーステンプレートを定義し、その上に子テンプレートを作成して独自のコンテンツを追加することができます。

Jinjaでベーステンプレートを定義するには、{% block %}タグを使用して、子テンプレートによって上書きできるコンテンツのブロックを定義する必要があります。次に、簡単なベーステンプレートの例を示します。

html
<!DOCTYPE html>
<html>
  <head>
    <title>{% block title %}Default Title{% endblock %}</title>
  </head>
  <body>
    <header>
      {% block header %}
      <nav>
        <ul>
          <li><a href="#">Home</a></li>
          <li><a href="#">About</a></li>
          <li><a href="#">Contact</a></li>
        </ul>
      </nav>
      {% endblock %}
    </header>
    <main>
      {% block content %}{% endblock %}
    </main>
    <footer>
      {% block footer %}
      &copy; 2023 My Site
      {% endblock %}
    </footer>
  </body>
</html>

この例では、{% block %}タグでtitleheaderfooterの3つのコンテンツブロックが定義されています。これらのブロックの内容は、子テンプレートによって上書きすることができます。

ベーステンプレートから継承した子テンプレートを作成するには、{% extends %}タグを使用してベーステンプレートの名前を指定し、{% block %}タグを使用してブロックの内容を上書きする必要があります。以下は、titlecontentブロックを上書きした子テンプレートの例です。

html
{% extends "base.html" %}

{% block title %}My Page Title{% endblock %}

{% block content %}
<h1>Welcome to my page!</h1>
<p>This is some content for my page.</p>
{% endblock %}

この例では、{% extends %}タグが子テンプレートがbase.htmlテンプレートを継承することを指定しています。{% block %}タグは、ベーステンプレートで定義されたtitlecontentブロックをオーバーライドします。

子テンプレートをレンダリングすると、Jinjaは自動的にベーステンプレートを起点として、オーバーライドされたブロックのコンテンツを挿入します。結果として生成されるHTMLには、ベーステンプレートで定義されたサイトの共通要素と、子テンプレートの特定のコンテンツが含まれます。

Jinja による SQL

以下にJinjaを使ってSQL文を記述する例を紹介します。

INSERT

以下は、Pythonで任意の数の行と列を持つSQL INSERTステートメントを生成するためにJinjaを使用する方法の例です。

python
import jinja2

# define your data
data = [
    ['Alice', 25, 'Female'],
    ['Bob', 30, 'Male'],
    ['Charlie', 40, 'Male'],
]

# define the template
template = """
INSERT INTO my_table (name, age, gender)
VALUES
{% for row in data %}
    (
    {% for col in row %}
        {% if col is string %}
        '{{ col }}' {% if not loop.last %},{% endif %}
        {% else %}
        {{ col }} {% if not loop.last %},{% endif %}
        {% endif %}
    {% endfor %}
    ) {% if not loop.last %},{% endif %}
{% endfor %}
"""

# compile the template
template = jinja2.Template(template)

# render the SQL statement with the data
sql_statement = template.render(data=data)

print(sql_statement)

この例では、まず、データをリストのリストとして定義します。各内側のリストは、テーブルに挿入するデータの行を表します。

次に、SQL挿入文をJinjaテンプレートとして定義します。データリスト内の各行を反復処理するためにforループを使用し、行内の各列を反復処理するために別のforループを使用します。 loop.last変数を使用して、列ごとにカンマを追加しますが、データ内の最後の列や最後の行の後にはカンマを追加しません。

また、現在の列の値が文字列かどうかを確認するために条件文を使用します。文字列である場合は、SQL文に挿入する前に引用符で囲みます。

最後に、Jinjaテンプレートをコンパイルし、データを使用してSQL文をレンダリングします。その結果得られたSQL文をデータベースに対して実行できます。

UPDATE

以下は、JinjaとPythonを使用してSQLテーブルを更新する例です。

python
import jinja2

# Define the data for the update
update_data = {
    'column1': 'new_value1',
    'column2': 'new_value2',
    'column3': 'new_value3'
}

# Define the Jinja template for the update query
update_query_template = """
    UPDATE my_table
    SET {% for column, value in update_data.items() %}
        {{ column }} = {{ value }}{% if not loop.last %},{% endif %}
    {% endfor %}
    WHERE id = {{ id }};
"""

# Create the Jinja environment and render the query template
env = jinja2.Environment()
update_query = env.from_string(update_query_template).render(update_data=update_data, id=123)

このコードでは、最初に更新に使用するデータを、列名とその新しい値の辞書として定義します。

次に、更新クエリのJinjaテンプレートを定義します。テンプレートの中で、forループを使用してupdate_data辞書内の列名と値を反復処理し、それぞれの列を対応する新しい値に設定します。loop.last変数を使用して、ループの最後の反復かどうかを確認し、SQL構文が正しいことを確認するために、最後の反復でない場合はカンマを追加します。

最後に、Jinja環境を作成し、クエリテンプレートをupdate_data辞書と更新する行のIDでレンダリングし、データベースコネクタを使用して生成されたクエリを実行します。

参考

https://jinja.palletsprojects.com/en/3.1.x/

Ryusei Kakujo

researchgatelinkedingithub

Focusing on data science for mobility

Bench Press 100kg!