mcpField
F
frappe-syntax-jinja

frappe-syntax-jinja

github.com/impertio-studio開発198123 回インストール30 週間前に更新
リポジトリ

frappe-syntax-jinja について

frappe-syntax-jinjaは、Frappeの印刷フォーマット、メール、通知、ポータルページ向けJinjaテンプレートのリファレンススキルです。構文やコンテキスト、バージョン別のレンダリング方法を扱います。

主な機能

  1. 01Jinja構文と制御構造
  2. 02テンプレート種別ごとのコンテキスト
  3. 03Frappeのメソッドとフィルター
  4. 04hooks.pyによる独自メソッドとフィルターの登録
  5. 05印刷フォーマットとポータルページのパターン
  6. 06v14~v16のレンダリング指針

活用シーン

  1. 01Frappeの印刷フォーマットの作成
  2. 02動的なメールまたは通知テンプレートの作成
  3. 03Pythonコントローラーを使ったポータルページの作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/impertio-studio/frappe_claude_skill_package --skill frappe-syntax-jinja

インストール後、Claude Code で/frappe-syntax-jinjaを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
anti-patterns.md
context-objects.md
examples.md
filters-reference.md
methods-reference.md
methods.md
patterns.md
structure.md
syntax.md
templates.md

詳細

Frappe Jinja テンプレートの構文

Frappe v14/v15/v16 の印刷フォーマット、メールテンプレート、通知テンプレート、ポータルページ向けの決定的な Jinja リファレンスです。


このスキルを使用する場合

次の場合に使用します:

  • Jinja ベースの印刷フォーマットを作成または変更する
  • 動的フィールドを使用するメールテンプレートを作成する
  • Python コントローラーを使用してポータルページ(www/*.html)を構築する
  • 通知テンプレート(システム/メール/SMS)を作成する
  • hooks.py を介してカスタム Jinja メソッドまたはフィルターを登録する

次の場合は使用しないでください:

  • レポートの印刷フォーマット — JavaScript テンプレート({%= %})を使用し、Jinja は使用しません
  • クライアントスクリプト — frappe-syntax-clientscripts を参照してください
  • サーバースクリプト — frappe-syntax-serverscripts を参照してください

決定ツリー:どのテンプレートタイプを選ぶか

Need a printable document?
├─ YES → Is it for a Query/Script Report?
│        ├─ YES → Use JS Template ({%= %}), NOT Jinja
│        └─ NO  → Use Jinja Print Format
└─ NO  → Is it for email?
         ├─ YES → Is it triggered by workflow/notification?
         │        ├─ YES → Notification Template (Jinja)
         │        └─ NO  → Email Template (Jinja)
         └─ NO  → Is it a web page?
                  ├─ YES → Portal Page (www/*.html + .py controller)
                  └─ NO  → frappe.render_template() for ad-hoc rendering

クイックリファレンス:Jinja の構文

構文用途例
{{ }}出力式{{ doc.name }}
{% %}制御文{% if doc.status == "Paid" %}
{# #}コメント{# This is a comment #}
{{ _("text") }}翻訳{{ _("Invoice") }}
{{ val | filter }}フィルター{{ name | default("N/A") }}
重要:Jinja と JS テンプレート構文の違い
項目Jinja(印刷フォーマット)JS テンプレート(レポートの印刷フォーマット)
出力{{ expression }}{%= expression %}
コードブロック{% statement %}{% js_code %}
言語PythonJavaScript
コンテキストdoc、frappedata、filters

レポートの印刷フォーマットで Jinja 構文を使用しないでください。標準の印刷フォーマットで {%= %} を使用しないでください。


テンプレートタイプ別のコンテキストオブジェクト

印刷フォーマット
オブジェクト説明
doc印刷対象のドキュメント(Document オブジェクト全体)
frappeFrappe モジュール(ホワイトリスト登録済みメソッドのみ)
frappe.utilsユーティリティ関数
_()翻訳関数
doc.items、doc.taxes子テーブルのアクセサー(フィールド名で指定)
メールテンプレート
オブジェクト説明
docリンクされたドキュメント(DocType からトリガーされた場合)
frappeFrappe モジュール(制限付き)
_()翻訳関数
通知テンプレート
オブジェクト説明
doc通知をトリガーしたドキュメント
frappeFrappe モジュール
_()翻訳関数
ポータルページ(www/*.html)
オブジェクト説明
frappeFrappe モジュール
frappe.session.user現在認証されているユーザー
frappe.form_dictURL のクエリパラメーター
frappe.lang現在の言語コード
カスタムコンテキスト.py コントローラーの get_context(context) で設定します

詳細:references/context-objects.md


必須メソッド(Jinja でホワイトリスト登録済み)

書式設定:表示には必ず使用
{# ALWAYS use get_formatted() for fields in Print Formats #}
{{ doc.get_formatted("posting_date") }}
{{ doc.get_formatted("grand_total") }}

{# Child table rows — ALWAYS pass parent doc for currency context #}
{% for row in doc.items %}
    {{ row.get_formatted("rate", doc) }}
    {{ row.get_formatted("amount", doc) }}
{% endfor %}

{# General formatting with explicit fieldtype #}
{{ frappe.format(value, {'fieldtype': 'Currency'}) }}
{{ frappe.format_date(doc.posting_date) }}
ドキュメントの取得
{# Full document — use only when multiple fields needed #}
{% set customer = frappe.get_doc("Customer", doc.customer) %}

{# Single field — ALWAYS prefer over get_doc for one field #}
{% set abbr = frappe.db.get_value("Company", doc.company, "abbr") %}

{# List of records (no permission check) #}
{% set tasks = frappe.get_all("Task",
    filters={"status": "Open"},
    fields=["title", "due_date"],
    order_by="due_date asc",
    page_length=10) %}

{# List with permission check (portal pages) #}
{% set orders = frappe.get_list("Sales Order",
    filters={"customer": doc.customer},
    fields=["name", "grand_total"]) %}
翻訳:ユーザー向け文字列すべてで必須
<h1>{{ _("Invoice") }}</h1>
<p>{{ _("Total: {0}").format(doc.get_formatted("grand_total")) }}</p>
システムとセッション
{{ frappe.get_url() }}
{{ frappe.get_fullname() }}
{{ frappe.get_fullname(doc.owner) }}
{{ frappe.db.get_single_value("System Settings", "time_zone") }}
{% if frappe.session.user != "Guest" %}...{% endif %}

メソッドの全リファレンス:references/methods-reference.md


制御構造

条件分岐
{% if doc.status == "Paid" %}
    <span class="paid">{{ _("Paid") }}</span>
{% elif doc.status == "Overdue" %}
    <span class="overdue">{{ _("Overdue") }}</span>
{% else %}
    <span>{{ doc.status }}</span>
{% endif %}
子テーブルを使用したループ
{% for item in doc.items %}
<tr>
    <td>{{ loop.index }}</td>
    <td>{{ item.item_name }}</td>
    <td>{{ item.get_formatted("amount", doc) }}</td>
</tr>
{% else %}
<tr><td colspan="3">{{ _("No items") }}</td></tr>
{% endfor %}
ループ変数
変数説明
loop.index1 から始まる位置
loop.index00 から始まる位置
loop.first最初の反復で True
loop.last最後の反復で True
loop.lengthアイテムの合計数
変数
{% set total = 0 %}
{% set name = doc.customer_name | default("Unknown") %}

フィルター

フィルター例説明
default{{ val | default("N/A") }}オプション項目には必ず使用します
length{{ items | length }}項目数を数えます
join{{ names | join(", ") }}リストを文字列に結合します
truncate{{ text | truncate(100) }}省略記号を付けて切り詰めます
escape{{ input | escape }}HTML エスケープします(デフォルトの動作)
safe{{ html | safe }}HTML をそのままレンダリングします。ユーザー入力には絶対に使用しないでください
round{{ num | round(2) }}数値を丸めます
lower / upper{{ text | upper }}大文字・小文字を変換します

フィルターの完全なリファレンス:references/filters-reference.md


hooks.py によるカスタム Jinja メソッドとフィルター

hooks.py の登録
# hooks.py
jenv = {
    "methods": [
        "myapp.jinja.methods"       # Module with callable functions
    ],
    "filters": [
        "myapp.jinja.filters"       # Module with filter functions
    ]
}
カスタムメソッド
# myapp/jinja/methods.py
import frappe

def get_company_logo(company):
    """Returns company logo URL. Called as get_company_logo() in Jinja."""
    return frappe.db.get_value("Company", company, "company_logo") or ""
<img src="{{ get_company_logo(doc.company) }}" alt="Logo">
カスタムフィルター
# myapp/jinja/filters.py
def nl2br(text):
    """Convert newlines to <br> tags. Used as {{ text | nl2br }}."""
    return (text or "").replace("\n", "<br>")
{{ doc.notes | nl2br | safe }}

詳細:references/methods.md


印刷フォーマットのパターン

最小限の印刷フォーマットテンプレート
<style>
    .print-header { background: #f5f5f5; padding: 15px; }
    .item-table { width: 100%; border-collapse: collapse; }
    .item-table th, .item-table td { border: 1px solid #ddd; padding: 8px; }
    .text-right { text-align: right; }
</style>

<div class="print-header">
    <h1>{{ doc.select_print_heading or _("Invoice") }}</h1>
    <p>{{ doc.name }} — {{ doc.get_formatted("posting_date") }}</p>
</div>

<table class="item-table">
    <thead>
        <tr>
            <th>#</th>
            <th>{{ _("Item") }}</th>
            <th class="text-right">{{ _("Qty") }}</th>
            <th class="text-right">{{ _("Amount") }}</th>
        </tr>
    </thead>
    <tbody>
        {% for row in doc.items %}
        <tr>
            <td>{{ loop.index }}</td>
            <td>{{ row.item_name }}</td>
            <td class="text-right">{{ row.qty }}</td>
            <td class="text-right">{{ row.get_formatted("amount", doc) }}</td>
        </tr>
        {% endfor %}
    </tbody>
</table>

<p><strong>{{ _("Grand Total") }}: {{ doc.get_formatted("grand_total") }}</strong></p>
改ページ
/* v14/v15 (wkhtmltopdf) */
.page-break { page-break-before: always; }

/* v16 (Chrome PDF) — ALWAYS prefer break-* in v16 */
.page-break { break-before: page; }

完全な例:references/examples.md | パターン:references/patterns.md


V16:Chrome による PDF レンダリング

項目v14/v15(wkhtmltopdf)v16(Chrome)
CSS サポート限定的な CSS3最新の CSS を全面的にサポート
Flexbox/Grid部分的全面的にサポート
改ページpage-break-*break-* を推奨
フォントシステムフォントのみWeb フォントをサポート
V16 の設定
// site_config.json
{
    "pdf_engine": "chrome",
    "chrome_path": "/usr/bin/chromium"
}

ポータルページのパターン

www/projects/index.html
{% extends "templates/web.html" %}
{% block title %}{{ _("Projects") }}{% endblock %}

{% block page_content %}
<h1>{{ _("Projects") }}</h1>
{% for project in projects %}
    <h3>{{ project.title }}</h3>
    <p>{{ project.description | default("") | truncate(150) }}</p>
{% else %}
    <p>{{ _("No projects found.") }}</p>
{% endfor %}
{% endblock %}
www/projects/index.py
import frappe

def get_context(context):
    context.title = "Projects"
    context.no_cache = True
    context.projects = frappe.get_all("Project",
        filters={"is_public": 1},
        fields=["name", "title", "description"],
        order_by="creation desc")
    return context

全体構成:references/structure.md | テンプレート:references/templates.md


重要なルール

必ず行うこと
  1. ユーザーに表示するすべての文字列に_()を使用します
  2. 通貨、日付、数値のフィールドにはget_formatted()を使用します
  3. オプションまたは null 許容のフィールドにはdefault()フィルターを使用します
  4. 子行のget_formatted("field", doc)には親のdocを渡します
  5. 1つのフィールドのみ必要な場合はfrappe.db.get_value()を使用します
  6. 計算は Jinja テンプレートではなく、Python コントローラーで行います
絶対に行わないこと
  1. ループ内でデータベースクエリを実行します(N+1 問題)
  2. ユーザー入力に| safeを使用します(XSS の脆弱性)
  3. レポートの印刷フォーマットで Jinja 構文を使用します(JS の{%= %}が必要です)
  4. frappe.db.get_value()で十分な場合にfrappe.get_doc()を使用します
  5. 翻訳用の_()で囲まずに文字列をハードコードします
  6. セキュリティレビューなしにsafe_renderを無効化します

アンチパターンと修正方法:references/anti-patterns.md


リファレンスファイル

ファイル内容
references/syntax.mdJinja 構文リファレンス(タグ、フィルター、テスト、ループ)
references/methods.mdhooks によるカスタム Jinja メソッド/フィルター
references/context-objects.mdテンプレートの種類ごとに使用できるオブジェクト
references/filters-reference.mdFrappe の標準およびカスタムフィルターすべて
references/methods-reference.mdJinja で使用できる frappe.* メソッドすべて
references/examples.md印刷フォーマット、メール、ポータルの完全な例
references/anti-patterns.mdよくある誤りと適切な代替方法
references/templates.mdテンプレート構造のパターン
references/patterns.md条件付きレンダリング、ループ、子テーブル
references/structure.mdテンプレートの種類ごとのファイル構成

関連項目

  • frappe-syntax-hooks — hooks.py の jenv 設定
  • frappe-impl-printformat — 印刷フォーマットの実装パターン
  • frappe-errors-serverscripts — サーバーサイドのエラー処理

コメント