数据库接口

版本 16 起,get_listget_all 有一些破坏性变更。

frappe.db.get_list

frappe.db.get_list(doctype, filters, or_filters, fields, order_by, group_by, start, page_length, run)

  • 也可用别名 frappe.get_list 调用

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.get_all 调用

frappe.db.get_list 相同,但会获取所有记录而不应用权限。

frappe.db.get_value

frappe.db.get_value(doctype, name, fieldname)frappe.db.get_value(doctype, filters, fieldname)

  • 也可用别名 frappe.get_valuefrappe.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)

此方法不会调用 validateon_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)

返回给定 doctypefilters 的记录数。

# 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

如果在类型为 POSTPUT 的 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 请求

  • 在执行 POSTPUT 时,如果对数据库进行了任何写入操作,这些操作将在请求成功结束时提交。
  • 使用 frappe.call 进行的 AJAX 调用默认是 POST,除非更改设置。
  • GET 请求不会触发隐式提交。
  • 请求处理期间发生的任何未捕获异常都将导致事务回滚。

后台/定时任务

  • 将函数作为后台或定时任务调用时,成功完成后将提交事务。
  • 任何未捕获异常都将导致事务回滚。

补丁(Patches)

  • 补丁的 execute 函数成功完成后,将自动提交事务。
  • 任何未捕获异常都将导致事务回滚。

单元测试

  • 运行一个测试模块后,事务将被提交。测试模块指的是任何 Python 测试文件,例如 test_core.py
  • 所有测试完成后,事务也会被提交。
  • 任何未捕获异常都将退出测试运行器,因此不会提交事务。

注意:如果您在任何地方捕获了异常,数据库抽象层将无法感知到错误的发生,因此您需要负责正确回滚事务。