app-development について
Frappe 開発者向けに、アプリの構成、フック、バックグラウンドジョブ、本番環境への準備を支援するスキルです。ビジネスロジックをサービス層に分け、Frappe の規約に沿った設計を扱います。
主な機能
- 01アプリのひな形と設計
- 02フック設定のパターン
- 03サービス層の構成
- 04バックグラウンドジョブの設定
- 05キャッシュとエラー処理のユーティリティ
- 06バージョン互換性の指針
活用シーン
- 01カスタム Frappe アプリの開始
- 02DocType コントローラーからのロジック分離
- 03イベントフックと定期ジョブの追加
- 04アプリのデプロイ準備
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ lubusin/ frappe-skills --skill app-developmentインストール後、Claude Code で/app-developmentを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/mini-app-template/your_app/api.pyhooks.pyREADME.mdassets/mini-app-template/your_app/background_jobs/sample_job.pyassets/mini-app-template/your_app/dashboard/sample_doc_dashboard.jsonassets/mini-app-template/your_app/doctype/sample_doc_item/sample_doc_item.jsonassets/mini-app-template/your_app/doctype/sample_doc/sample_doc_dynamic_link.jsonsample_doc_naming_series.jsonsample_doc.jssample_doc.jsonsample_doc.pyassets/mini-app-template/your_app/doctype/sample_single/sample_single.jsonassets/mini-app-template/your_app/doctype/sample_submittable/sample_submittable.jsonassets/mini-app-template/your_app/doctype/sample_tree/sample_tree.jsonassets/mini-app-template/your_app/integrations/sample_connector.pyassets/mini-app-template/your_app/list_view/sample_doc_list.jsassets/mini-app-template/your_app/report/sample_report/sample_report.jssample_report.jsonsample_report.pyassets/mini-app-template/your_app/services/sample_service.pyassets/mini-app-template/your_app/utils/cache.pyerrors.pylogging.pypermissions.pyvalidation.pyassets/mini-app-template/your_app/workflow/sample_doc_workflow.jsonreferences/app-development.mdtranslations.mdversion-compat.md詳細
Frappe アプリ開発
本番環境に対応したパターンを用いて、カスタム Frappe アプリをスキャフォールドし、構成し、設計します。
使用する場面
- 新しいカスタム Frappe アプリをゼロから作成する場合
- アプリのアーキテクチャ(モジュール、サービス、ユーティリティ)を設定する場合
- イベント、スケジューラー、オーバーライド用に
hooks.pyを設定する場合 - バックグラウンドジョブと非同期処理を実装する場合
- サービスレイヤーとドメインロジックのパターンを構築する場合
- キャッシュ、ロギング、エラー処理のユーティリティを追加する場合
- アプリを本番環境へのデプロイに備える場合
- 翻訳、バージョン管理、パッケージ化を管理する場合
必要な入力
- アプリ名と目的
- 対象となる Frappe のバージョン(v13+、v15+、v16+)
- モジュール構成(DocType、API、サービス)
- 他のアプリのフック/オーバーライドが必要かどうか
- バックグラウンドジョブの要件
- 本番環境への対応要件
手順
0) アプリをスキャフォールドする
# Create the app
bench new-app my_app
# Install on site
bench --site mysite.local install-app my_app
# Verify developer mode
bench --site mysite.local console
>>> frappe.conf.developer_mode # Must be True
1) アプリの構成を計画する
ドメインアーキテクチャのパターンに従い、DocType コントローラーは薄く保ち、ビジネスロジックはサービスモジュールに配置します。
my_app/
├── my_app/
│ ├── __init__.py
│ ├── hooks.py # App hooks and configuration
│ ├── api.py # Public API surface (RPC endpoints)
│ ├── services/ # Business logic modules
│ │ └── billing.py
│ ├── utils/ # Cross-cutting utilities
│ │ ├── cache.py
│ │ ├── errors.py
│ │ ├── logging.py
│ │ ├── permissions.py
│ │ └── validation.py
│ ├── background_jobs/ # Async job handlers
│ │ └── export_job.py
│ ├── integrations/ # External system connectors
│ │ └── payment_gateway.py
│ ├── my_module/
│ │ ├── doctype/
│ │ │ └── my_doc/
│ │ │ ├── my_doc.json
│ │ │ ├── my_doc.py
│ │ │ ├── my_doc.js
│ │ │ └── test_my_doc.py
│ │ ├── report/
│ │ └── dashboard/
│ └── translations/
│ ├── en.csv
│ └── fr.csv
├── setup.py
└── README.md
出発点となるスキャフォールドとして、assets/mini-app-template/ の mini-app-template を使用します。
2) hooks.py を設定する
# hooks.py
app_name = "my_app"
app_title = "My App"
app_publisher = "My Company"
# DocType lifecycle events
doc_events = {
"Sales Order": {
"on_submit": "my_app.services.billing.on_order_submit",
"on_cancel": "my_app.services.billing.on_order_cancel",
},
"*": {
"after_insert": "my_app.utils.logging.log_creation",
}
}
# Scheduled tasks
scheduler_events = {
"daily": [
"my_app.background_jobs.cleanup.run_daily_cleanup"
],
"cron": {
"0 */6 * * *": [
"my_app.background_jobs.sync.sync_external_data"
]
}
}
# Client-side script injection
doctype_js = {
"Sales Order": "public/js/sales_order.js"
}
doctype_list_js = {
"Sales Order": "public/js/sales_order_list.js"
}
# Override another app's controller (use sparingly)
# override_doctype_class = {
# "ToDo": "my_app.overrides.custom_todo.CustomToDo"
# }
# Extend controller without full replacement (v16+, preferred)
# extend_doctype_class = {
# "ToDo": "my_app.overrides.todo_extension.TodoExtension"
# }
# Override whitelisted methods
# override_whitelisted_methods = {
# "frappe.client.get_list": "my_app.overrides.custom_get_list"
# }
3) サービスレイヤーを実装する
DocType コントローラーは薄く保ち、ビジネスロジックはサービスに委譲します。
# my_app/services/billing.py
import frappe
def on_order_submit(doc, method):
"""Handle order submission — called via doc_events hook."""
if doc.grand_total > 10000:
create_approval_request(doc)
generate_invoice(doc)
def generate_invoice(order):
"""Create invoice from submitted order."""
invoice = frappe.get_doc({
"doctype": "Sales Invoice",
"customer": order.customer,
"items": [
{"item_code": i.item_code, "qty": i.qty, "rate": i.rate}
for i in order.items
]
})
invoice.insert()
invoice.submit()
return invoice
4) バックグラウンドジョブを設定する
# my_app/background_jobs/export_job.py
import frappe
def enqueue_export(filters):
"""Enqueue a long-running export job."""
frappe.enqueue(
"my_app.background_jobs.export_job.run_export",
filters=filters,
queue="long",
timeout=600,
is_async=True
)
def run_export(filters):
"""Execute the export — runs in background worker."""
data = frappe.get_all("Sales Order", filters=filters, fields=["*"])
# Process data...
frappe.publish_realtime("export_complete", {"count": len(data)})
5) 横断的なユーティリティを追加する
# my_app/utils/cache.py
import frappe
def get_cached_settings(key):
"""Cache expensive settings lookups."""
value = frappe.cache().get_value(f"my_app:{key}")
if value is None:
value = frappe.db.get_single_value("My Settings", key)
frappe.cache().set_value(f"my_app:{key}", value)
return value
def invalidate_cache(key):
frappe.cache().delete_value(f"my_app:{key}")
# my_app/utils/errors.py
import frappe
def api_error(message, status_code=400, exc=None):
"""Consistent error response for API endpoints."""
frappe.local.response["http_status_code"] = status_code
frappe.throw(message, exc or frappe.ValidationError)
6) 翻訳に対応する
# In Python code
frappe._("Hello World") # Mark string for translation
# In JavaScript
__("Hello World") # Mark string for translation
# Translation CSV files go in my_app/translations/
# e.g., my_app/translations/fr.csv:
# Hello World,Bonjour le monde
7) バージョン互換性
| 機能 | 最小バージョン |
|---|---|
extend_doctype_class | Frappe v16+ |
REST API v2 (/api/v2/) | Frappe v15+ |
| トークンベース認証 | Frappe v11.0.3+ |
複数のバージョンを対象とする場合は、バージョン固有の機能を条件分岐で保護します。
import frappe
if hasattr(frappe, 'extend_doctype_class'):
# v16+ pattern
pass
else:
# Fallback for older versions
pass
検証
- アプリがエラーなくインストールできる:
bench --site <site> install-app my_app - フックが正しく実行される(スケジューラーログ、ドキュメントイベントを確認)
- バックグラウンドジョブがキューに追加され、完了する
-
bench --site <site> migrateが成功する - テストが合格する:
bench --site <site> run-tests --app my_app
失敗しやすい点/デバッグ
- アプリが見つからない:
apps.txtとsites/<site>/site_config.jsonを確認します - フックが実行されない:
hooks.pyの構文を確認し、bench を再起動します - バックグラウンドジョブが停止する:
bench doctorでワーカーの状態を確認し、Redis を確認します - インポートエラー:hooks 内のモジュールパスが実際の Python パスと一致していることを確認します
- 開発者モードがオフ:DocType の変更はファイルにエクスポートされません
エスカレーション
- DocType の作成方法 →
doctype-development - API エンドポイントのパターン →
api-development - Desk UI のカスタマイズ →
desk-customization - Frappe UI フロントエンド →
frontend-development - 印刷フォーマットと Jinja →
printing-templates - レポート →
reports - ウェブフォーム →
web-forms - テスト →
testing - エンタープライズアーキテクチャ →
enterprise-patterns - Docker/FM 環境 →
frappe-manager
参考資料
- references/app-development.md — アプリのエンドツーエンドアーキテクチャ
- references/version-compat.md — バージョン互換性に関する注意事項
- references/translations.md — 多言語サポート
相互参照(専門スキルが担当)
- hooks.py と拡張ポイント →
doctype-development(hooks-extensions.md) - Python API リファレンス →
api-development(python-api.md)
ガードレール
- カスタムフロントエンドには Frappe UI を使用します:バニラ JavaScript、jQuery、カスタムフレームワークは使用しないでください。Frappe UI(Vue 3 + TailwindCSS)はエコシステムの標準です。セットアップについては
frontend-developmentを参照してください。 - CRUD アプリには CRM/Helpdesk のパターンに従います:公式 Frappe アプリを基にしたアプリシェル、ナビゲーション、リストビュー、フォームレイアウトには、
ui-patternsスキルを使用してください。 - 命名規則に従います:アプリ名は小文字とアンダースコアを使用し、有効な Python 識別子である必要があります。
- 統合には hooks.py を使用します:モンキーパッチは使用せず、doc_events、scheduler_events、boot_session フックを使用してください。
- hooks.py を簡潔に保ちます:設定のみを記述し、ロジックは含めないでください。モジュールからインポートします。
- 後方互換性を維持します:バージョンをまたいでサポートする場合は、
frappe.versionによるチェックを使用してください。 - フィクスチャを適切にエクスポートします:アプリと同期するデータには、hooks.py の
fixturesを使用してください。
よくある間違い
| 間違い | 失敗する理由 | 修正方法 |
|---|---|---|
アプリが installed_apps にない | アプリのコードが読み込まれない | bench --site <site> install-app my_app を実行します |
| hooks 内のモジュールパスが間違っている | イベントが発火しない | パスが実際の my_app/module/file.py の構造と一致していることを確認します |
| フックが重複して登録されている | イベントが複数回発火する | hooks.py に重複がないか確認し、キーを繰り返すのではなくリストを使用します |
| 再起動せずに hooks.py を編集する | 変更が反映されない | hooks.py の変更後に bench restart を実行します |
__init__.py ファイルが不足している | モジュールのインポートエラーが発生する | すべてのディレクトリに __init__.py があることを確認します |
| hooks.py にロジックを記述する | テストが難しくなり、インポートエラーが発生する | ロジックを別のモジュールに移し、フック内でインポートします |
| バニラ JavaScript/jQuery でフロントエンドを構築する | エコシステムとの一貫性がなくなる | Frappe UI(Vue 3)を使用してください。frontend-development を参照してください |
| CRUD アプリにカスタムアプリシェルを使用する | UX に一貫性がなくなる | ナビゲーションとレイアウトには CRM/Helpdesk のパターンに従います |
コメント
Markdown 対応