版本 16 起,get_list 和 get_all 有一些破坏性变更。
frappe.db.get_list
frappe.db.get_list(doctype, filters, or_filters, fields, order_by, group_by, start, page_length, run)
从 doctype 表中返回记录列表。这是 SELECT 查询的 ORM 封装。它还会为当前会话用户应用记录的用户权限。如果未提供 fields 关键字参数,则仅返回文档名称。默认情况下,此方法返回一个 dict 列表,但您可以通过提供 pluck 关键字参数来提取特定字段:
frappe.db.get_list('Employee')
# output
[{'name': 'HR-EMP-00008'},
{'name': 'HR-EMP-00006'},
{'name': 'HR-EMP-00010'},
{'name': 'HR-EMP-00005'}
]
# with pluck
frappe.db.get_list('Employee', pluck='name')
# output
['HR-EMP-00008',
'HR-EMP-00006',
'HR-EMP-00010',
'HR-EMP-00005'
]
组合过滤器和其他参数:
frappe.db.get_list('Task',
filters={
'status': 'Open'
},
fields=['subject', 'date'],
order_by='date desc',
start=10,
page_length=20,
as_list=True
)
# output
(('Update Branding and Design', '2019-09-04'),
('Missing Documentation', '2019-09-02'),
('Fundraiser for Foundation', '2019-09-03'))
# Tasks with date after 2019-09-08
frappe.db.get_list('Task', filters={
'date': ['>', '2019-09-08']
})
# Tasks with date between 2020-04-01 and 2021-03-31 (both inclusive)
frappe.db.get_list('Task', filters=[[
'date', 'between', ['2020-04-01', '2021-03-31']
]])
# Tasks with subject that contains "test"
frappe.db.get_list('Task', filters={
'subject': ['like', '%test%']
})
# Count number of tasks grouped by status
frappe.db.get_list('Task',
fields=['count(name) as count', 'status'],
group_by='status'
)
# For version 16 +
frappe.db.get_list('Task',
fields=[{'COUNT': 'name', 'as': 'count'}, 'status'],
group_by='status'
)
# output
[{'count': 1, 'status': 'Working'},
{'count': 2, 'status': 'Overdue'},
{'count': 2, 'status': 'Open'},
{'count': 1, 'status': 'Filed'},
{'count': 20, 'status': 'Completed'},
{'count': 1, 'status': 'Cancelled'}]
在版本 16 之前,设置 run=False 会返回 SQL 查询而不是执行它。从版本 16 开始,您将获得一个查询构建器对象。您可以像以前一样调用 .get_sql() 来获取 SQL 查询,否则您可以在运行之前按需修改查询(query.run())。
frappe.db.get_all
frappe.db.get_all(doctype, filters, or_filters, fields, order_by, group_by, start, page_length, run)
与 frappe.db.get_list 相同,但会获取所有记录而不应用权限。
frappe.db.get_value
frappe.db.get_value(doctype, name, fieldname) 或 frappe.db.get_value(doctype, filters, fieldname)
- 也可用别名
frappe.get_value 和 frappe.db.get_values 调用
返回文档的字段值或值列表。
# single value
subject = frappe.db.get_value('Task', 'TASK00002', 'subject')
# multiple values
subject, description = frappe.db.get_value('Task', 'TASK00002', ['subject', 'description'])
# as dict
task_dict = frappe.db.get_value('Task', 'TASK00002', ['subject', 'description'], as_dict=1)
task_dict.subject
task_dict.description
# with filters, will return the first record that matches filters
subject, description = frappe.db.get_value('Task', {'status': 'Open'}, ['subject', 'description'])
frappe.db.getsinglevalue
frappe.db.get_single_value(doctype, fieldname)
从单个 DocType 返回字段值。
timezone = frappe.db.get_single_value('System Settings', 'timezone')
frappe.db.set_value
frappe.db.set_value(doctype, name, fieldname, value)
- 也可用别名
frappe.db.update 调用
在数据库中设置字段值,不会调用 ORM 触发器,但会更新修改时间戳(除非指定不更新)。
# update a field value
frappe.db.set_value('Task', 'TASK00002', 'subject', 'New Subject')
# update multiple values
frappe.db.set_value('Task', 'TASK00002', {
'subject': 'New Subject',
'description': 'New Description'
})
# update without updating the `modified` timestamp
frappe.db.set_value('Task', 'TASK00002', 'subject', 'New Subject', update_modified=False)
此方法不会调用 validate 和 on_update 等 ORM 触发器。使用此方法更新隐藏字段,或当您清楚自己在做什么时使用。
frappe.db.exists
frappe.db.exists(doctype, name)
如果文档记录存在,则返回 true。
传入 doctype 和 docname:
frappe.db.exists("User", "[email protected]", cache=True)
传入包含 "doctype" 键的过滤器字典:
frappe.db.exists({"doctype": "User", "full_name": "Jane Doe"})
传入 doctype 和过滤器字典:
frappe.db.exists("User", {"full_name": "Jane Doe"})
frappe.db.count
frappe.db.count(doctype, filters)
返回给定 doctype 和 filters 的记录数。
# total number of Task records
frappe.db.count('Task')
# total number of Open tasks
frappe.db.count('Task', {'status': 'Open'})
frappe.db.delete
frappe.db.delete(doctype, filters)
删除与 filters 匹配的 doctype 记录。这会执行 DML 命令,这意味着它可以回滚。如果未指定过滤器,则会删除该 doctype 的所有记录。
frappe.db.delete("Route History", {
"modified": ("<=", last_record_to_keep[0].modified),
"user": user
})
frappe.db.delete("Error Log")
frappe.db.delete("__Test Table")
您可以传入 doctype 名称或内部表名。按照惯例,Frappe 中的内部表以 __ 为前缀。API 遵循此约定。上述命令在表 tabError Log 和 __Test Table 上运行无条件的 DELETE 查询。
frappe.db.truncate
frappe.db.truncate(doctype)
截断数据库中的表。这会执行 DDL 命令 TRUNCATE TABLE,在执行语句之前会触发提交。此操作无法回滚。您可能希望使用它来定期清理日志表。
frappe.db.truncate("Error Log")
frappe.db.truncate("__Test Table")
上述命令在表 tabError Log 和 __Test Table 上运行 TRUNCATE 查询。
frappe.db.commit
frappe.db.commit()
提交当前事务。调用 SQL COMMIT。
在大多数情况下,您无需手动提交。请参阅下面的 Frappe 数据库事务模型。
frappe.db.savepoint
frappe.db.savepoint(save_point)
创建一个命名保存点,您可以稍后回滚到该点。
frappe.db.rollback
frappe.db.rollback()
回滚当前事务。调用 SQL ROLLBACK。
如果在类型为 POST 或 PUT 的 Web 请求期间抛出异常,Frappe 将自动运行 frappe.db.rollback()。如果您需要在事务中提前回滚,请使用此方法。
frappe.db.rollback(save_point="save_point_name")
回滚到特定保存点,而不是回滚整个事务。此回滚不会撤销对文件系统以及任何其他回滚监视器所做的更改。
frappe.db.sql
frappe.db.sql(query, values, as_dict)
执行任意 SQL 查询。这对于包含连接语句的复杂服务端报表、调整数据库以适应新功能等可能很有用。
示例:
values = {'company': 'Frappe Technologies Inc'}
data = frappe.db.sql("""
SELECT
acc.account_number
gl.debit
gl.credit
FROM `tabGL Entry` gl
LEFT JOIN `tabAccount` acc
ON gl.account = acc.name
WHERE gl.company = %(company)s
""", values=values, as_dict=0)
避免使用此方法,因为它会绕过验证和完整性检查。如果可能,最好使用 frappe.get_doc、frappe.db.get_list 等。
frappe.db.multisql
frappe.db.multisql({'mariadb': mariadb_query, 'postgres': postgres_query})
为任何受支持的数据库引擎执行合适的 SQL 语句。
frappe.db.rename_table
frappe.db.rename_table(old_name, new_name)
执行查询以更改表名。直接指定 DocType 或内部表名来重命名表。
示例:
frappe.db.rename_table("__internal_cache", "__temporary_cache")
frappe.db.rename_table("todo", "ToDo")
仅当您了解其影响时才应使用第二个示例。
不要使用此方法重命名 DocType 表。请改用 frappe.rename_doc 来实现。
frappe.db.describe
frappe.db.describe(doctype)
返回指定 DocType 的表结构描述元组。
frappe.db.changecolumntype
frappe.db.change_column_type(doctype, column, new_type)
更改指定 DocType 的列类型。
frappe.db.add_index
frappe.db.add_index(doctype, fields, index_name)
为指定字段的 DocType 创建索引。
注意:如果需要对 TEXT 或 BLOB 字段创建索引,必须指定固定长度。
示例:
frappe.db.add_index("Notes", ["id(10)", "content(500)"], index_name)
frappe.db.add_unique
frappe.db.add_unique(doctype, fields, constraint_name=None)
为指定字段的 DocType 创建唯一约束。
示例:
frappe.db.add_unique("DoctypeName",["field1","field2"])
frappe.db.bulk_update
frappe.db.bulk_update(doctype, doc_updates, *, chunk_size=100, modified=None, modified_by=None, update_modified=True, debug=False)
使用带有 CASE 表达式的单个 SQL UPDATE 语句,批量更新多个文档。
示例:
frappe.db.bulk_update(
"Task",
{
"TASK-0001": {"status": "Closed", "description": "Completed by QA"},
"TASK-0002": {"status": "Open", "description": "Pending assignment"},
},
chunk_size=200,
modified_by="[email protected]",
update_modified=True,
debug=True,
)
doc_updates 格式:
{
"DOC-0001": {"field1": "value1", "field2": "value2"},
"DOC-0002": {"field1": "valueA", "field2": "valueB"},
}
注意事项:
- 直接更新数据库;不会触发文档事件或验证。
- 未指定的字段保持不变。
数据库事务钩子
注意:此 API 在 v15 版本中引入。
Frappe 提供了钩子,用于在发出提交(commit)或回滚(rollback)等事务命令之前或之后运行回调。这些钩子适用于以下场景:
- 如果事务被回滚,则回滚数据库之外所做的更改。
- 仅在事务提交后,才将数据库之外的更改刷新。
这些钩子包括:
frappe.db.before_commit.add(func: Callable)
frappe.db.after_commit.add(func: Callable)
frappe.db.before_rollback.add(func: Callable)
frappe.db.after_rollback.add(func: Callable)
用法示例:
def create_file(self):
self.write_file()
# This ensures rollback if DB transaction is rolledback
frappe.db.after_rollback.add(self.rollback_file)
def rollback_file(self):
self.delete_file()
数据库事务模型
Frappe 的数据库抽象层默认实现了一个合理的事务模型。因此,在大多数情况下,您无需手动处理 SQL 事务。该模型的简要描述如下:
Web 请求
- 在执行
POST 或 PUT 时,如果对数据库进行了任何写入操作,这些操作将在请求成功结束时提交。
- 使用
frappe.call 进行的 AJAX 调用默认是 POST,除非更改设置。
GET 请求不会触发隐式提交。
- 请求处理期间发生的任何未捕获异常都将导致事务回滚。
后台/定时任务
- 将函数作为后台或定时任务调用时,成功完成后将提交事务。
- 任何未捕获异常都将导致事务回滚。
补丁(Patches)
- 补丁的
execute 函数成功完成后,将自动提交事务。
- 任何未捕获异常都将导致事务回滚。
单元测试
- 运行一个测试模块后,事务将被提交。测试模块指的是任何 Python 测试文件,例如
test_core.py。
- 所有测试完成后,事务也会被提交。
- 任何未捕获异常都将退出测试运行器,因此不会提交事务。
注意:如果您在任何地方捕获了异常,数据库抽象层将无法感知到错误的发生,因此您需要负责正确回滚事务。