Jinja テンプレート言語
概要
Docwize はテンプレート構築に Jinja を使用しています。Word ドキュメント、動的スタンプ、メールテンプレートなどのテンプレートが作成できます。Jinja のテンプレートは、変数と式(値を表示する)およびタグ(ロジックを制御する)を組み合わせたテキストファイルです。HTML や XML などのテキストベースのフォーマットを生成するための設計図と考えてください。
Jinja の詳細な概要については、https://jinja.palletsprojects.com/en/stable/templates/# をご覧ください。
Jinja テンプレートの作成時には、お好みの AI アシスタントの利用をお勧めします。
主要な概念
変数
テンプレートが使用される際に実際の値に置き換えられるプレースホルダーです。
o ドット (.) または角括弧 ([]) を使って変数の一部にアクセスできます。例:{{ user.name }} または {{ user['name'] }}。
o 変数が存在しない場合、エラーが発生します。
タグ
テンプレートの動作を制御します。
o ステートメント ({% ... %}): ループ (for) や条件 (if) などのアクション用。
o 式 ({{ ... }}): 値を表示するため。
o コメント ({# ... #}): 最終出力に表示されないメモ用。
フィルター
変数を変換します。パイプ | を使って適用します。
o 例:{{ name|striptags|title }} は HTML を除去し、各単語の先頭を大文字にします。
テスト
変数が特定の条件を満たすかどうかを確認します。is を使って適用します。
o 例:{% if name is defined %} は name が存在するかどうかを確認します。
Template Inheritance(テンプレートの継承)
この強力な機能により、共通部分(ヘッダーやフッターなど)を含むベーステンプレートを作成し、特定のセクションを埋める「子」テンプレートを作ることができます。
o {% block content %}: 子テンプレートが埋めたり変更したりできるセクションをテンプレートに定義します。
o {{ super() }}: 子ブロック内でこれを使うと、親テンプレートの同じブロックのコンテンツが呼び出されます。
式
値を生成するコードの一部です。テンプレート内で動的なコンテンツを表示するために使用されます。テンプレートロジックの「出力」部分と考えてください。タグがフロー(if、for、set)を制御するのに対し、式は実際にデータをユーザーに表示するものです。
構文
式は常に二重中括弧で囲まれます:{{ ... }}
式の仕組みと内容
変数の表示
式の最も一般的な使い方は、テンプレートに渡された変数の値を単純に出力することです。例:
{{ document.description}}: ドキュメントの説明が利用可能な場合、ドキュメントの説明名が出力されます。
{{ document[reference_number]}}: 括弧記法を使ってディクショナリ(または属性)から値にアクセスします。
フィルターの使用
前述のとおり、式は変数の出力を変換するためにフィルターを適用する場所です。
{{ document.doc_type | upper }}: ドキュメントタイプの変数を出力前にすべて大文字に変換します。
{{ document.description | striptags }}: ドキュメントの説明の変数から HTML タグを除去します。
{{ document.date | default("N/A") }}: ドキュメントの日付を表示します。利用できない場合は "N/A" を表示します。
基本的な数学演算
式の中で直接、簡単な算術演算を行うことができます。複雑な計算は通常テンプレートにデータを送る前に処理するのが最善ですが、基本的な数学はサポートされています。
{{ 10 / 3 }}: 3.333... を表示します(浮動小数点除算)。
{{ 10 // 3 }}: 3 を表示します(整数除算)。
{{ 5 % 2 }}: 1 を表示します(余り)。
注意:ここで提供されている特定のドキュメントプロパティは、通常このような数学的計算には使用されません。上記の例は Jinja の一般的な数学演算を示したものです。
文字列の連結
チルダ ~ 演算子を使って文字列を結合できます。動的な文字列の構築に便利です。
{{ "Document " ~ document.doc_number ~ " from " ~ document.from_person ~ " (" ~ document.from_entity ~ ")" }}:
"Document INV-001 from John Doe (Acme Corp)" のような結合された文字列を出力します。
メソッド/関数の呼び出し(値を返すもの)
変数がメソッドを持つオブジェクトである場合、式内でそのメソッドを呼び出すことができ、メソッド呼び出しの結果が表示されます。
{{ document.description.capitalize() }}: document.description が "initial draft" の場合、"Initial draft" と出力される可能性があります。
リテラル
文字列、数値、リスト、ディクショナリなどの値を直接含めることができます。これらはフィルターや関数の引数としてよく使用されます。
{{ "This is a literal string" }}
{{ 12345 }}
{{ ['Invoice', 'Contract', 'Report'] | join(', ') }}: ドキュメントタイプのリテラルリストを使用し、カンマとスペースで結合するフィルターを適用します。
If 式(三項演算子)
条件が真の場合と偽の場合で異なる値を表示するための簡潔な方法です。
{{ "Document approved" if document.doc.status == "approved" else "Document pending" }}: document.doc.status が 'approved' の場合は "Document approved" を表示し、そうでない場合は "Document pending" を表示します。
空白の制御
Jinja は通常ほとんどの空白を保持しますが、制御することができます。
タグの区切り文字の隣にマイナス記号 (-) を追加すると空白が除去されます:
{%- for item in seq %} は先頭の空白を除去します。
{{ item }}{%- endfor %} は末尾の空白を除去します。
共通の制御構造
For ループ: リスト内の各アイテムに対してコンテンツを繰り返します。
<ul>
{% for document in documents%}
<li>{{ document.description|e }}</li>
{% else %}
<li>ドキュメントが見つかりません。</li>
{% endfor %}
</ul>
特別なループ変数(現在のカウントを表す loop.index など)にアクセスできます。
リストが空の場合のコンテンツには {% else %} を使用します。
If ステートメント: 条件に基づいてコンテンツを表示します。
{% if document.doc_status == 'approved' %}
このドキュメントは承認され、公開準備が整っています。
{% elif document.doc_status == 'pending_review' %}
このドキュメントは現在チームによるレビューを待っています。
{% elif document.type == 'draft' %}
これは下書きドキュメントです。最終化の前にさらなる作業が必要です。
{% else %}
このドキュメントのステータスは不明であるか、注意が必要です。
{% endif %}
マクロ: 関数のような再利用可能なコードの断片です。
{% macro display_doc_info_simple(doc_number, doc_type, doc_status) %}
Document: {{ doc_number | default("N/A") }{ | Type: {{ doc_type | default("N/A") }{ | Status: {{ doc_status | default("N/A") }{
{% endmacro %}
マクロが別のファイルにある場合は、最初にインポートする必要があります。
Jinja コードの例と結果
Docwize 内での Jinja コードの例と、その動作および出力の説明を以下に示します:
1. 変数の表示とフィルターの適用
これらの例は、特定のドキュメント属性を出力し、書式設定またはサニタイズのためのフィルターを適用する方法を示しています。
例 A: デフォルト値付きのドキュメント番号の表示
{{ document.doc_number | default("N/A") }}
説明: この式は document 変数から doc_number 属性の値を出力します。default("N/A") フィルターにより、document.doc_number が None、空文字列、または未定義の場合に "N/A" の文字列が表示され、欠損データの空白出力を防ぎます。
結果:
document が {'doc_number': 'INV-2024-001'} の場合: INV-2024-001
document が {'doc_number': None} または {'doc_number': ''} の場合: N/A
document に doc_number 属性がない場合: N/A
例 B: 日付の書式設定と HTML エスケープの適用
{{ document.date | date('%Y-%m-%d') | e }}
説明: この式は document 変数の date 属性を処理します。
date('%Y-%m-%d'): このカスタムフィルター(Jinja 環境で datetime オブジェクト用に定義または暗黙的に処理されると仮定)は、日付を "YYYY-MM-DD" 形式の文字列に変換します。
e(または escape): このビルトインフィルターは結果の文字列を HTML エスケープします。日付文字列が HTML として誤解される可能性のある文字を含む場合に重要で、クロスサイトスクリプティング(XSS)の脆弱性を防ぎます。
結果:
document.date が 2024年5月27日を表す datetime オブジェクトの場合: 2024-05-27
document.date が "2024-05-27<script>alert('XSS')</script>" の場合(date フィルターが除去しない場合): 2024-05-27<script>alert('XSS')</script>(e フィルターにより安全になります)
例 C: ドキュメントタイプの大文字化とタグの除去
{{ document.doc_type | striptags | title }}
説明: この式は doc_type 属性を取得し、striptags フィルター(HTML タグを除去)を適用した後、title フィルター(各単語の先頭を大文字にする)を適用します。
結果:
document.doc_type が "purchase.order" の場合: Purchase Order
document.doc_type が "invoice" の場合: Invoice
2. For ループの例
この例は、アイテムのリスト(ドキュメントの明細行項目など)を反復処理し、そのプロパティを表示する方法を示しています。
<h4>ドキュメントの明細行項目:</h4>
<ul>
{% for item in document.line_items %}
<li>{{ item.description | default("説明なし") }{ - 数量: {{ item.quantity | default(0) }{ - 価格: ${{ item.price | default(0.0) | round(2) }{</li>
{% else %}
<li>このドキュメントに明細行項目が見つかりません。</li>
{% endfor %}
</ul>
説明: これは item オブジェクトのリストを反復処理する for ループタグです(document.line_items がテンプレートに渡されたリストであると仮定)。各 item に対して、description、quantity、price を表示する <li> 要素を出力します。堅牢性のために default フィルターを使用し、価格の書式設定に round(2) を使用します。document.line_items リストが空の場合は {% else %} ブロックが実行されます。
結果:
シナリオ 1: document.line_items が [{'description': 'Laptop', 'quantity': 1, 'price': 1200.50}, {'description': 'Mouse', 'quantity': 2, 'price': 25}] の場合
<h4>ドキュメントの明細行項目:</h4>
<ul>
<li>Laptop - 数量: 1 - 価格: $1200.50</li>
<li>Mouse - 数量: 2 - 価格: $25.00</li>
</ul>
シナリオ 2: document.line_items が [](空のリスト)の場合
<h4>ドキュメントの明細行項目:</h4>
<ul>
<li>このドキュメントに明細行項目が見つかりません。</li>
</ul>
シナリオ 3: document.line_items にデータが欠損しているアイテムが含まれる場合: [{'Keyboard'}, {'quantity': 5, 'price': 10.00}]
<h4>ドキュメントの明細行項目:</h4>
<ul>
<li>Keyboard - 数量: 0 - 価格: $0.00</li>
<li>説明なし - 数量: 5 - 価格: $10.00</li>
</ul>
3. If ステートメントの例
この例は、ドキュメントのステータスやタイプに基づいて条件付きで情報を表示するために if ステートメントを使用しています。
{% if document.doc_status == 'Approved' %}
<p style="color: green;">このドキュメントは承認されました。</p>
{% elif document.doc_status == 'Pending' %}
<p style="color: orange;">このドキュメントは現在レビュー待ちです。</p>
{% elif document.doc_status == 'Rejected' %}
<p style="color: red;">このドキュメントは却下されました。管理者にお問い合わせください。</p>
{% else %}
<p>ドキュメントのステータス: {{ document.doc_status | default("不明") }{。</p>
{% endif %}
{% if document.doc_type == 'Invoice' and document.reference_number %}
<p>請求書番号: {{ document.reference_number }{</p>
{% endif %}
説明: 最初のブロックは document.doc_status の値を確認する if-elif-else ステートメントです。ドキュメントが「Approved」、「Pending」、「Rejected」、またはその他のステータスかどうかに基づいて異なるメッセージを表示します。2番目の if ステートメントは、doc_type が 'Invoice' かつ reference_number が存在する(真値である)場合にのみ参照番号を表示します。
結果:
シナリオ 1: document が {'doc_status': 'Approved'} の場合
<p style="color: green;">このドキュメントは承認されました。</p>
シナリオ 2: document が {'doc_status': 'Pending'} の場合
<p style="color: orange;">このドキュメントは現在レビュー待ちです。</p>
シナリオ 3: document が {'doc_status': 'Rejected'} の場合
<p style="color: red;">このドキュメントは却下されました。管理者にお問い合わせください。</p>
シナリオ 4: document が {'doc_status': 'Draft'} の場合
<p>ドキュメントのステータス: Draft。</p>
シナリオ 5: document が {'doc_type': 'Invoice', 'reference_number': 'INV-REF-007'} の場合(ステータスの例と組み合わせた場合、例えば「Approved」)
<p style="color: green;">このドキュメントは承認されました。</p>
<p>請求書番号: INV-REF-007</p>
シナリオ 6: document が {'doc_type': 'Purchase Order', 'reference_number': 'PO-REF-123'} の場合(doc_type が 'Invoice' でないため、2番目の if 条件は偽になります)
4. マクロの例
マクロは再利用可能なコンポーネントを定義できます。ここでは、ドキュメントプロパティに対して一貫した表示ブロックを生成するためにマクロが使用されています。
マクロの定義:
{% macro display_document_property(label, value, default_value="N/A") %}
<div class="document-property">
<span class="label">{{ label }{:</span>
<span class="value">{{ value | default(default_value) }{</span>
</div>
{% endmacro %}
説明: これは display_document_property という名前のマクロを定義します。3つの引数を取ります:
label: プロパティの人間が読める名前(例:「Document Number」)。
value: 表示する値の実際の変数または属性(例:document.doc_number)。
default_value: value が未定義または空の場合に使用される、デフォルトが "N/A" のオプション引数。マクロ内では、label と value の <span> タグを含む <div> 要素を生成し、value に default フィルターを適用します。
結果(定義の場合): マクロ自体は呼び出されるまで出力を生成しません。
マクロの呼び出し:
{{ display_document_property("Document Number", document.doc_number) }}
{{ display_document_property("Document Type", document.doc_type) }}
{{ display_document_property("Date", document.date | date('%Y-%m-%d')) }}
{{ display_document_property("Document Status", document.doc_status, "Undetermined") }}
{{ display_document_property("Reference Number", document.reference_number) }}
説明: これらの行は display_document_property マクロの呼び出しを示しています。
- 最初の2つの呼び出しはデフォルトの "N/A" フォールバックを使用します。
- 3番目の呼び出しは書式設定のために date フィルターを適用した後の document.date を渡します。
- 4番目の呼び出しは default_value を明示的に "Undetermined" に上書きします。
結果(呼び出しの場合):
シナリオ 1: document が {'doc_number': 'ORD-567', 'doc_type': 'Order', 'date': datetime.date(2024, 1, 15), 'doc_status': 'Completed', 'reference_number': None} の場合
<div class="document-property">
<span class="label">Document Number:</span>
<span class="value">ORD-567</span>
</div>
<div class="document-property">
<span class="label">Document Type:</span>
<span class="value">Order</span>
</div>
<div class="document-property">
<span class="label">Date:</span>
<span class="value">2024-01-15</span>
</div>
<div class="document-property">
<span class="label">Document Status:</span>
<span class="value">Completed</span>
</div>
<div class="document-property">
<span class="label">Reference Number:</span>
<span class="value">N/A</span>
</div>
シナリオ 2: document が {'doc_number': None, 'doc_type': 'Quote', 'date': None, 'doc_status': 'Draft', 'reference_number': 'Q-001'} の場合
<div class="document-property">
<span class="label">Document Number:</span>
<span class="value">N/A</span>
</div>
<div class="document-property">
<span class="label">Document Type:</span>
<span class="value">Quote</span>
</div>
<div class="document-property">
<span class="label">Date:</span>
<span class="value">N/A</span>
</div>