mcpField
F
frappe-syntax-customapp

frappe-syntax-customapp

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

frappe-syntax-customapp について

Frappe のカスタムアプリを作成する開発者向けの構文リファレンスです。アプリ構成、設定、モジュール、パッチ、Fixture を扱い、v14 と v15 以降の違いや導入・移行のルールも説明します。

主な機能

  1. 01アプリ作成・導入コマンドの確認
  2. 02ディレクトリ構成と主要設定ファイルの確認
  3. 03モジュール、DocType、パッチの設定
  4. 04Fixture の設定、エクスポート、インポート
  5. 05バージョンごとの設定差分の比較

活用シーン

  1. 01アプリの作成・取得コマンドを調べる
  2. 02モジュールと DocType の配置を計画
  3. 03データ移行パッチを作成
  4. 04カスタムフィールド用 Fixture を設定

インストール

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

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

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

同梱ファイル

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

references/
anti-patterns.md
examples.md
fixtures.md
methods.md
modules.md
patches.md
patterns.md
pyproject-toml.md
structure.md
syntax.md
templates.md

詳細

Frappe カスタムアプリの構文

Frappe カスタムアプリを構築するための決定論的な構文リファレンス — スキャフォールディング、設定、モジュール、パッチ、フィクスチャ。

意思決定ツリー

What do you need?
├─ Brand new app from scratch → bench new-app
├─ Extend existing ERPNext behavior → bench new-app + required_apps = ["frappe", "erpnext"]
├─ Install existing app from Git → bench get-app <url>
└─ Add functionality to an installed app
   ├─ New data model → Add module to modules.txt + create DocType
   ├─ New fields on existing DocType → Fixtures (Custom Field)
   ├─ Modify field properties → Fixtures (Property Setter)
   └─ Data migration → Patch in patches.txt

New app vs extend existing?
├─ Independent functionality → New app
├─ Tightly coupled to one app → New app with required_apps dependency
└─ Small customization (fields, properties) → Extend via fixtures in existing custom app

アプリの作成

# Create new app (interactive prompts for title, description, publisher, etc.)
bench new-app my_custom_app

# Install on site
bench --site mysite install-app my_custom_app

# Get existing app from Git
bench get-app https://github.com/org/my_custom_app

# Build frontend assets
bench build --app my_custom_app

# Run migrations (patches + fixtures + schema sync)
bench --site mysite migrate

アプリのディレクトリ構成

[v15+] pyproject.toml(推奨)
apps/my_custom_app/
├── pyproject.toml                     # Build configuration (flit)
├── README.md
├── my_custom_app/                     # Inner Python package
│   ├── __init__.py                    # MUST contain __version__
│   ├── hooks.py                       # Frappe integration hooks
│   ├── modules.txt                    # Module registration
│   ├── patches.txt                    # Migration scripts
│   ├── patches/                       # Patch files
│   │   └── __init__.py
│   ├── my_custom_app/                 # Default module (same name as app)
│   │   ├── __init__.py
│   │   └── doctype/
│   ├── public/                        # Static assets → /assets/my_custom_app/
│   │   ├── css/
│   │   └── js/
│   ├── templates/                     # Jinja templates
│   │   └── includes/
│   └── www/                           # Portal pages (URL = directory path)
└── .git/
[v14] setup.py(レガシー)
apps/my_custom_app/
├── setup.py                           # Build configuration (setuptools)
├── MANIFEST.in
├── requirements.txt                   # Python dependencies
├── dev-requirements.txt               # Dev dependencies (developer_mode only)
├── package.json                       # Node dependencies
├── my_custom_app/
│   ├── __init__.py
│   ├── hooks.py
│   ├── modules.txt
│   ├── patches.txt
│   └── [same inner structure as v15]
└── .git/

重要なファイル

init.py(必須)
# my_custom_app/__init__.py
__version__ = "0.0.1"

重要:__version__ がないと、flit のビルドは失敗し、アプリをインストールできません。

pyproject.toml [v15+]
[build-system]
requires = ["flit_core >=3.4,<4"]
build-backend = "flit_core.buildapi"

[project]
name = "my_custom_app"
authors = [
    { name = "Your Company", email = "[email protected]" }
]
description = "Description of your app"
requires-python = ">=3.10"
readme = "README.md"
dynamic = ["version"]
dependencies = []            # Python packages ONLY — NEVER Frappe/ERPNext

[tool.bench.frappe-dependencies]
frappe = ">=15.0.0,<16.0.0"
erpnext = ">=15.0.0,<16.0.0"  # Only if app extends ERPNext

pyproject.toml の重要なルール:

  • name は内側のディレクトリ名と完全に一致する必要があります
  • dynamic = ["version"] は必須です — flit は __init__.py から __version__ を読み取ります
  • [project] dependencies に frappe または erpnext を絶対に記述しないでください(PyPI にはありません)
  • Frappe アプリの依存関係は必ず [tool.bench.frappe-dependencies] に記述してください
setup.py [v14](レガシー)
from setuptools import setup, find_packages

setup(
    name="my_custom_app",
    version="0.0.1",
    description="Description of your app",
    author="Your Company",
    author_email="[email protected]",
    packages=find_packages(),
    zip_safe=False,
    include_package_data=True,
    install_requires=[],
)
hooks.py(最小限のスケルトン)
app_name = "my_custom_app"
app_title = "My Custom App"
app_publisher = "Your Company"
app_description = "Description"
app_email = "[email protected]"
app_license = "MIT"

required_apps = ["frappe"]  # Or ["frappe", "erpnext"] if extending ERPNext

fixtures = [
    {"dt": "Custom Field", "filters": [["module", "=", "My Custom App"]]},
    {"dt": "Property Setter", "filters": [["module", "=", "My Custom App"]]},
]

モジュール

modules.txt
My Custom App
Integrations
Settings
Reports

ルール:

  • 1 行につきモジュール名を 1 つ記述します — 空行や末尾のスペースは絶対に残さないでください
  • モジュール名にはスペースを使用し、ディレクトリ名にはアンダースコアを使用します(My Custom App → my_custom_app/)
  • すべての DocType は登録済みのモジュールに属している必要があります
  • すべてのモジュールディレクトリに必ず __init__.py を含めてください
モジュールのディレクトリ構成
my_custom_app/
├── my_custom_app/       # "My Custom App" module
│   ├── __init__.py
│   └── doctype/
├── integrations/        # "Integrations" module
│   ├── __init__.py
│   └── doctype/
├── settings/            # "Settings" module
│   ├── __init__.py
│   └── doctype/
└── reports/             # "Reports" module
    ├── __init__.py
    └── report/
DocType ディレクトリ(モジュール内)
doctype/my_doctype/
├── __init__.py              # Empty (REQUIRED)
├── my_doctype.json          # DocType definition (generated by UI)
├── my_doctype.py            # Python controller
├── my_doctype.js            # Client script
├── test_my_doctype.py       # Unit tests
└── my_doctype_dashboard.py  # Dashboard config

パッチ(移行スクリプト)

INI セクションを含む patches.txt
[pre_model_sync]
# Runs BEFORE schema sync — old fields still available
myapp.patches.v1_0.backup_old_data

[post_model_sync]
# Runs AFTER schema sync — new fields available
myapp.patches.v1_0.populate_new_fields
myapp.patches.v1_0.cleanup_data
パッチの実装
# myapp/patches/v1_0/populate_new_fields.py
import frappe

def execute():
    """Populate new fields with default values."""
    batch_size = 1000
    offset = 0

    while True:
        records = frappe.get_all(
            "MyDocType",
            filters={"new_field": ["is", "not set"]},
            fields=["name"],
            limit_page_length=batch_size,
            limit_start=offset,
        )
        if not records:
            break

        for record in records:
            frappe.db.set_value(
                "MyDocType", record.name,
                "new_field", "default_value",
                update_modified=False,
            )

        frappe.db.commit()
        offset += batch_size
モデル同期の前と後
状況セクション理由
古いフィールドからデータを移行する[pre_model_sync]古いフィールドがまだ存在するため
フィールド名を変更してデータを保持する[pre_model_sync]古い名前をまだ使用できるため
新しい必須フィールドに値を設定する[post_model_sync]新しいフィールドがすでに存在するため
一般的なデータクリーンアップ[post_model_sync]スキーマへの依存がないため
パッチの再実行
# Patches run ONCE. To re-run, make the line unique with a comment:
myapp.patches.v1_0.my_patch #2024-01-15
bench migrate のワークフロー
  1. before_migrate フックを実行
  2. [pre_model_sync] パッチを実行
  3. データベーススキーマを同期(DocType JSON → テーブル)
  4. [post_model_sync] パッチを実行
  5. フィクスチャを同期
  6. after_migrate フックを実行

フィクスチャ

hooks.py の設定
fixtures = [
    "Category",                                              # All records
    {"dt": "Custom Field", "filters": [["module", "=", "My Custom App"]]},
    {"dt": "Property Setter", "filters": [["module", "=", "My Custom App"]]},
    {"dt": "Role", "filters": [["name", "like", "MyApp%"]]},
]
エクスポートとインポート
# Export fixtures to JSON files
bench --site mysite export-fixtures --app my_custom_app

# Import happens automatically during bench migrate or install-app
フィクスチャとパッチの比較
対象フィクスチャパッチ
カスタムフィールドはいいいえ
プロパティセッターはいいいえ
ロール、ワークフローはいいいえ
データ変換いいえはい
1 回限りの移行いいえはい
設定データのシードはいいいえ
フィクスチャの順序

依存関係のあるものが先になるよう、フィクスチャの順序を必ず設定してください。

fixtures = [
    "Workflow State",   # FIRST — Workflow depends on states
    "Workflow",         # SECOND
]

バージョンの違い

項目v14v15+v16+
ビルド設定setup.pypyproject.tomlpyproject.toml
ビルドバックエンドsetuptoolsflit_coreflit_core
依存関係ファイルrequirements.txtpyproject.tomlpyproject.toml
Python の最小バージョン>=3.10>=3.10>=3.14
INI パッチはいはいはい
v14 から v15 への移行
  1. flit_core の build-system を含む pyproject.toml を作成します
  2. 依存関係を requirements.txt から [project] dependencies に移します
  3. __init__.py の __version__ を確認します
  4. 必要に応じて、setup.py、MANIFEST.in、requirements.txt を削除します
  5. bench get-app と bench install-app でテストします

重要なルール

必ず行うこと
  1. __init__.py に __version__ を定義します — ないと flit のビルドに失敗します
  2. pyproject.toml に dynamic = ["version"] を追加します
  3. すべてのモジュールを modules.txt に登録します
  4. すべての Python ディレクトリに __init__.py を含めます
  5. Frappe の依存関係は [tool.bench.frappe-dependencies] に記述し、絶対に [project] dependencies には記述しません
  6. パッチではバッチ処理とエラー処理を使用します
  7. フィクスチャを正しくエクスポートするため、カスタムフィールドとプロパティセッターに module フィールドを設定します
  8. 依存関係に従ってフィクスチャの順序を設定します(ワークフローより前に状態を配置します)
絶対にしてはいけないこと
  1. frappe または erpnext を pip の依存関係に含めないでください(PyPI にないため、インストールに失敗します)
  2. try/except とロギングなしでパッチを作成しないでください
  3. フィクスチャにユーザーデータや取引データ(Sales Invoice、User)を含めないでください
  4. パッチにサイト固有の値をハードコードしないでください
  5. バッチ処理と定期的な frappe.db.commit() を行わずに、大規模なデータセットを処理しないでください
  6. ディレクトリ名にスペースを使わないでください(modules.txt 内のスペースのみ可)
  7. DocType が本番環境で作成された後に、モジュール名を変更しないでください

参考ファイル

ファイル内容
structure.mdv14 と v15 の完全なディレクトリ構造
pyproject-toml.mdpyproject.toml と setup.py の完全な設定
modules.mdモジュールの構成、命名、ワークスペース
patches.mdパッチの構文、モデル同期の前後、バッチ処理
fixtures.mdフィクスチャの設定、フィルター、一般的な DocType
examples.md最小構成の完全な例と ERPNext 拡張アプリの例
anti-patterns.mdよくある間違いトップ 10 とその修正方法

関連項目

  • frappe-syntax-hooks — hooks.py の完全なリファレンス
  • frappe-syntax-controllers — DocType コントローラーのメソッド
  • frappe-impl-customapp — 実装パターンとワークフロー

コメント