frappe-syntax-customapp について
Frappe のカスタムアプリを作成する開発者向けの構文リファレンスです。アプリ構成、設定、モジュール、パッチ、Fixture を扱い、v14 と v15 以降の違いや導入・移行のルールも説明します。
主な機能
- 01アプリ作成・導入コマンドの確認
- 02ディレクトリ構成と主要設定ファイルの確認
- 03モジュール、DocType、パッチの設定
- 04Fixture の設定、エクスポート、インポート
- 05バージョンごとの設定差分の比較
活用シーン
- 01アプリの作成・取得コマンドを調べる
- 02モジュールと DocType の配置を計画
- 03データ移行パッチを作成
- 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.mdexamples.mdfixtures.mdmethods.mdmodules.mdpatches.mdpatterns.mdpyproject-toml.mdstructure.mdsyntax.mdtemplates.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 のワークフロー
before_migrateフックを実行[pre_model_sync]パッチを実行- データベーススキーマを同期(DocType JSON → テーブル)
[post_model_sync]パッチを実行- フィクスチャを同期
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
]
バージョンの違い
| 項目 | v14 | v15+ | v16+ |
|---|---|---|---|
| ビルド設定 | setup.py | pyproject.toml | pyproject.toml |
| ビルドバックエンド | setuptools | flit_core | flit_core |
| 依存関係ファイル | requirements.txt | pyproject.toml | pyproject.toml |
| Python の最小バージョン | >=3.10 | >=3.10 | >=3.14 |
| INI パッチ | はい | はい | はい |
v14 から v15 への移行
- flit_core の build-system を含む
pyproject.tomlを作成します - 依存関係を
requirements.txtから[project] dependenciesに移します __init__.pyの__version__を確認します- 必要に応じて、
setup.py、MANIFEST.in、requirements.txtを削除します bench get-appとbench install-appでテストします
重要なルール
必ず行うこと
__init__.pyに__version__を定義します — ないと flit のビルドに失敗します- pyproject.toml に
dynamic = ["version"]を追加します - すべてのモジュールを
modules.txtに登録します - すべての Python ディレクトリに
__init__.pyを含めます - Frappe の依存関係は
[tool.bench.frappe-dependencies]に記述し、絶対に[project] dependenciesには記述しません - パッチではバッチ処理とエラー処理を使用します
- フィクスチャを正しくエクスポートするため、カスタムフィールドとプロパティセッターに
moduleフィールドを設定します - 依存関係に従ってフィクスチャの順序を設定します(ワークフローより前に状態を配置します)
絶対にしてはいけないこと
frappeまたはerpnextを pip の依存関係に含めないでください(PyPI にないため、インストールに失敗します)- try/except とロギングなしでパッチを作成しないでください
- フィクスチャにユーザーデータや取引データ(Sales Invoice、User)を含めないでください
- パッチにサイト固有の値をハードコードしないでください
- バッチ処理と定期的な
frappe.db.commit()を行わずに、大規模なデータセットを処理しないでください - ディレクトリ名にスペースを使わないでください(
modules.txt内のスペースのみ可) - DocType が本番環境で作成された後に、モジュール名を変更しないでください
参考ファイル
| ファイル | 内容 |
|---|---|
| structure.md | v14 と v15 の完全なディレクトリ構造 |
| pyproject-toml.md | pyproject.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— 実装パターンとワークフロー
コメント
Markdown 対応