关于 frappe-app-development
frappe-app-development 是帮助开发者规划和构建自定义 Frappe 应用的技能,适用于从脚手架搭建到生产部署准备。内容涵盖分层架构、钩子、后台作业、通用工具及版本兼容性。
主要功能
- 01搭建应用脚手架并规划结构
- 02配置 hooks.py 与服务层
- 03设置后台作业和通用工具
- 04处理翻译及版本兼容性
- 05提供验证与故障排查指引
适用场景
- 01从零开始创建自定义 Frappe 应用
- 02组织 DocType、服务和业务逻辑
- 03为应用添加钩子及异步处理
- 04检查安装、迁移和后台作业问题
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ lubusin/ frappe-skills --skill frappe-app-development安装后在 Claude Code 中输入/frappe-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检查 worker 状态;确认 Redis 正常 - 导入错误:确保 hooks 中的模块路径与实际 Python 路径一致
- 未开启开发者模式:DocType 更改不会导出到文件
升级处理
- 有关 DocType 创建详情 →
frappe-doctype-development - 有关 API 端点模式 →
frappe-api-development - 有关 Desk UI 自定义 →
frappe-desk-customization - 有关 Frappe UI 前端 →
frappe-frontend-development - 有关打印格式和 Jinja →
frappe-printing-templates - 有关报表 →
frappe-reports - 有关 Web 表单 →
frappe-web-forms - 有关测试 →
frappe-testing - 有关企业架构 →
frappe-enterprise-patterns - 有关 Docker/FM 环境 →
frappe-manager
参考资料
- references/app-development.md — 端到端应用架构
- references/version-compat.md — 版本兼容性说明
- references/translations.md — 多语言支持
交叉引用(由专门技能维护)
- hooks.py 和扩展点 →
frappe-doctype-development(hooks-extensions.md) - Python API 参考 →
frappe-api-development(python-api.md)
防护规则
- 自定义前端使用 Frappe UI:切勿使用原生 JS、jQuery 或自定义框架。Frappe UI(Vue 3 + TailwindCSS)是生态系统标准。设置方法请参阅
frappe-frontend-development。 - CRUD 应用遵循 CRM/Helpdesk 模式:遵循
frappe-ui-patterns技能,采用源自官方 Frappe 应用的应用外壳、导航、列表视图和表单布局。 - 遵循命名约定:应用名称必须使用小写字母和下划线,并且是有效的 Python 标识符
- 使用 hooks.py 进行集成:切勿猴子补丁;使用 doc_events、scheduler_events、boot_session 钩子
- 保持 hooks.py 简洁:只放配置,不放逻辑;从模块导入
- 保持向后兼容性:使用
frappe.version检查以支持不同版本 - 正确导出 fixtures:对于需要与应用同步的数据,在 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 中编写逻辑 | 难以测试,且会出现导入错误 | 将逻辑移至单独的模块,并在 hooks 中导入 |
| 使用原生 JS/jQuery 构建前端 | 与生态系统不一致 | 使用 Frappe UI(Vue 3);参阅 frappe-frontend-development |
| 为 CRUD 应用自定义应用外壳 | 用户体验不一致 | 遵循 CRM/Helpdesk 的导航和布局模式 |
评论
支持 Markdown