# 控制器
控制器是一个普通的Python类,它扩展自frappe.model.Document基类。这个基类是DocType的核心逻辑。它处理如何从数据库加载值,如何解析并将它们保存回数据库。
当您创建一个名为Person的DocType时,会创建一个名为person.py的Python文件,内容如下:
import frappe
from frappe.model.document import Document
class Person(Document):
pass
2
3
4
5
6
类中所有字段都可作为属性使用。
# 控制器方法
您可以在控制器中添加自定义方法,并通过doc对象调用。例如,
# 控制器类
class Person(Document):
def get_full_name(self):
"""返回人的全名"""
return f"{self.first_name} {self.last_name}"
# 在代码中的某处
>>> doc = frappe.get_doc("Person", "000001")
>>> doc.get_full_name()
John Doe
2
3
4
5
6
7
8
9
10
11
# 控制器钩子
为了在文档的生命周期中添加自定义行为,我们有控制器钩子。
| 方法名称 | 描述 | 插入 | 保存 | 提交 | 取消 | 提交后更新 |
|---|---|---|---|---|---|---|
before_insert | 文档准备插入前调用 | X | ||||
before_naming | 文档的name属性设置前调用 | X | ||||
autoname | 如果在控制器中定义,此方法用于设置文档的name属性 | X | ||||
before_validate | 验证前调用此钩子,用于自动设置缺失的值 | X | X | X | ||
validate | 使用此方法抛出任何验证错误并阻止文档保存 | X | X | X | ||
before_save | 文档保存前调用此方法 | X | X | |||
before_submit | 文档提交前调用此方法 | X | ||||
before_cancel | 文档取消前调用此方法 | X | ||||
before_update_after_submit | 当提交的文档字段更新时调用此方法 | X | ||||
db_insert | 此方法将文档插入数据库,除非您正在处理虚拟DocType,否则不要覆盖此方法 | X | ||||
after_insert | 文档插入数据库后调用 | X | ||||
db_update | 此方法更新数据库中的文档,除非您正在处理虚拟DocType,否则不要覆盖此方法 | X | X | X | X | |
on_update | 当现有文档的值更新时调用 | X | X | |||
on_submit | 当文档提交时调用 | X | ||||
on_cancel | 当提交的文档被取消时调用 | X | ||||
on_update_after_submit | 提交的文档值更新时调用 | X | ||||
on_change | 当文档的值发生变化时调用。此方法在执行db_set时也会被调用,因此在这个方法中执行的操作应该是幂等的 | X | X | X | X | X |
除了典型动作的文档事件外,您还可以钩住其他动作。
| 方法名称 | 描述 |
|---|---|
before_rename | 文档重命名前调用 |
after_rename | 文档重命名后调用 |
on_trash | 文档被删除时调用 |
after_delete | 文档被删除后调用 |
要使用控制器钩子,只需定义一个具有该名称的类方法。例如
class Person(Document):
def validate(self):
if self.age <= 18:
frappe.throw("Person's age must be at least 18")
def after_insert(self):
frappe.sendmail(recipients=[self.email], message="Thank you for registering!")
2
3
4
5
6
7
8
如果钩子不足以满足您的需求,您还可以覆盖预定义的文档方法以添加您自己的行为。例如要覆盖save()方法,
class Person(Document):
def save(self, *args, **kwargs):
super().save(*args, **kwargs) # 调用基类保存方法
do_something() # 例如:触发一个API调用或一个记录"用户X尝试更新这个特定记录"的旋转文件记录器
2
3
4
5
doc对象提供了许多默认方法。您可以在此处找到完整列表。
# 1. 创建文档
要创建一个新文档并将其保存到数据库,
doc = frappe.get_doc({
'doctype': 'Person',
'first_name': 'John',
'last_name': 'Doe'
})
doc.insert()
doc.name # 000001
2
3
4
5
6
7
8
9
# 2. 加载文档
要从数据库中获取现有文档,
doc = frappe.get_doc('Person', '000001')
# doctype 字段
doc.first_name # John
doc.last_name # Doe
# 标准字段
doc.creation # datetime.datetime(2018, 9, 20, 12, 39, 34, 236801)
doc.owner # john.doe@frappeframework.com
2
3
4
5
6
7
8
9
10
# 文档
文档是DocType的一个实例。它通常映射数据库表中的一行。我们在代码中称之为doc。
示例
假设我们有一个名为ToDo的DocType,包含以下字段:
descriptionstatuspriority
现在,如果我们想从数据库中查询一个文档,我们可以使用ORM。
>>> doc = frappe.get_doc("ToDo", "0000001")
<todo: 0000001="">
>>> doc.as_dict()
{'name': '0000001',
'owner': 'Administrator',
'creation': datetime.datetime(2022, 3, 28, 18, 20, 23, 275229),
'modified': datetime.datetime(2022, 3, 28, 18, 20, 23, 275229),
'modified_by': 'Administrator',
'docstatus': 0,
'idx': 0,
'status': 'Open',
'priority': 'Medium',
'color': None,
'date': None,
'allocated_to': None,
'description': 'Test',
'reference_type': None,
'reference_name': None,
'role': None,
'assigned_by': 'Administrator',
'assigned_by_full_name': 'Administrator',
'sender': None,
'assignment_rule': None,
'doctype': 'ToDo'}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
您可以获得description、status和priority的值,但您还会获得像creation、owner和modified_by这样的字段,这些字段是由框架在所有docs上默认添加的。
# 类型注释
从版本15开始引入。
Frappe支持在控制器文件中自动生成Python类型注释。这些注释可用于控制器文件内的自动完成、引用和类型检查。
class Person(Document):
# 自动生成类型
# 此代码是自动生成的。不要在此代码块中修改任何内容。
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
first_name: DF.Data
last_name: DF.Data
user: DF.Link
pass
2
3
4
5
6
7
8
9
10
11
12
13
14
注意:在创建或更新doctypes时生成这些注释。如果您修改了代码块,它将在下次更新时被覆盖。
您可以通过在应用中添加以下钩子来配置自动导出。
# hooks.py
export_python_type_annotations = True
2
3
了解更多关于类型注释的信息:
https://docs.python.org/3/library/typing.html
VS Code用户可以安装Python扩展以获得更好的自动完成 - https://code.visualstudio.com/docs/languages/python
大多数其他编辑器都有等效的插件系统,使用LSP。