测试

Frappe 提供了一些基本工具来编写自动化测试。以下是一些基本规则:

  1. 测试可以放在仓库中的任何位置,但必须以 test_ 开头,并且应该是一个 .py 文件。
  2. 测试运行器会自动为依赖的 DocType 构建测试记录,这些 DocType 由 Link 类型字段(外键)标识。
  3. 对于非 DocType 测试,你可以编写简单的单元测试,并在文件名前加上 test_。

编写测试

当你创建一个新的 DocType(在开发者模式下)时,样板文件中也包含 test_{doctype}.py 文件。测试文件应该负责创建依赖项并清理它们。

以下是一个参考自 test_event.py 的示例测试文件。

import frappe
import unittest

def create_events():
 if frappe.flags.test_events_created:
 return

 frappe.set_user("Administrator")
 doc = frappe.get_doc({
 "doctype": "Event",
 "subject":"_Test Event 1",
 "starts_on": "2014-01-01",
 "event_type": "Public"
 }).insert()

 doc = frappe.get_doc({
 "doctype": "Event",
 "subject":"_Test Event 3",
 "starts_on": "2014-01-01",
 "event_type": "Public"
 "event_individuals": [{
 "person": "test1@example.com"
 }]
 }).insert()

 frappe.flags.test_events_created = True

class TestEvent(unittest.TestCase):
 def setUp(self):
 create_events()

 def tearDown(self):
 frappe.set_user("Administrator")

 def test_allowed_public(self):
 frappe.set_user("test1@example.com")
 doc = frappe.get_doc("Event", frappe.db.get_value("Event",
 {"subject":"_Test Event 1"}))
 self.assertTrue(frappe.has_permission("Event", doc=doc))

 def test_not_allowed_private(self):
 frappe.set_user("test1@example.com")
 doc = frappe.get_doc("Event", frappe.db.get_value("Event",
 {"subject":"_Test Event 2"}))
 self.assertFalse(frappe.has_permission("Event", doc=doc))

为命令编写测试

要为你的 Bench 命令编写测试,你可以将测试分组到一个类中,该类继承自 frappe.tests.test_commands 中的 BaseTestCommands 和 unittest.TestCase,以便它在 bench run-tests 命令期间运行。

作为参考,以下是为 bench execute 命令编写的一些测试。

class TestCommands(BaseTestCommands, unittest.TestCase):
 def test_execute(self):
 # test 1: execute a command expecting a numeric output
 self.execute("bench --site {site} execute frappe.db.get_database_size")
 self.assertEqual(self.returncode, 0)
 self.assertIsInstance(float(self.stdout), float)

 # test 2: execute a command expecting an errored output as local won't exist
 self.execute("bench --site {site} execute frappe.local.site")
 self.assertEqual(self.returncode, 1)
 self.assertIsNotNone(self.stderr)

 # test 3: execute a command with kwargs
 # Note:
 # terminal command has been escaped to avoid .format string replacement
 # The returned value has quotes which have been trimmed for the test
 self.execute("""bench --site {site} execute frappe.bold --kwargs '{{"text": "DocType"}}'""")
 self.assertEqual(self.returncode, 0)
 self.assertEqual(self.stdout[1:-1], frappe.bold(text='DocType'))

运行测试

运行测试可能需要应用在其 dev-requirements.txt 文件中指定的额外依赖项。在运行测试之前,请确保所有应用都已使用 bench setup requirements --dev 安装了开发依赖项。

运行以下命令来运行所有测试。它会先构建所有测试依赖项一次,然后运行你的测试。你应该从 frappe_bench 文件夹中运行测试。

# run all tests
bench --site [sitename] run-tests

# run tests for only frappe app
bench --site [sitename] run-tests --app frappe

# run tests for the Task doctype
bench --site [sitename] run-tests --doctype "Task"

# run tests for All doctypes in specified Module Def
bench --site [sitename] run-tests --module-def "Contacts"

# run a test using module path
bench --site [sitename] run-tests --module frappe.tests.test_api

# run a specific test from a test file
bench --site [sitename] run-tests --module frappe.tests.test_api --test test_insert_many

# run tests without creating test records
bench --site [sitename] run-tests --skip-test-records --doctype "Task"

# profile tests and show a report after tests execute
bench --site [sitename] run-tests --profile --doctype "Task"
.
----------------------------------------------------------------------
Ran 1 test in 0.010s

OK

9133 function calls (8912 primitive calls) in 0.011 seconds

Ordered by: cumulative time

ncalls tottime percall cumtime percall filename:lineno(function)
 2 0.000 0.000 0.008 0.004 /home/frappe/frappe-bench/apps/frappe/frappe/model/document.py:187(insert)
 1 0.000 0.000 0.003 0.003 /home/frappe/frappe-bench/apps/frappe/frappe/model/document.py:386(_validate)
 13 0.000 0.000 0.002 0.000 /home/frappe/frappe-bench/apps/frappe/frappe/database.py:77(sql)
 255 0.000 0.000 0.002 0.000 /home/frappe/frappe-bench/apps/frappe/frappe/model/base_document.py:91(get)
 12 0.000 0.000 0.002 0.000

# verbose log level for tests
bench --site [sitename] --verbose run-tests

检查测试是否正在运行

你可以通过评估 frappe.in_test 全局变量来检查你的代码是否作为测试的一部分被执行。在较旧的框架版本中,你可以改为评估 frappe.flags.in_test(这将在即将发布的版本中被弃用)。

你可以使用该全局变量根据测试是否正在运行来在代码中添加分支。例如:

if not frappe.in_test:
    notify_error_to_user()

并行运行测试

随着项目中测试数量的增长,如果在一台机器上串行运行,测试完成需要很长时间。
在多台测试机器上并行运行测试可以在持续集成(CI)中节省时间。

并行测试

命令:

bench --site [sitename] --app [app-name] run-parallel-tests --build-id <build-number> --total-build <total-number-of-builds>

用法:

如果你想在 2 个 CI 实例上运行测试,你的命令如下:

# in first CI instance
bench --site [sitename] run-parallel-tests --build-id 1 --total-builds 2

# in second CI instance
bench --site [sitename] run-parallel-tests --build-id 2 --total-builds 2

注意:该命令会将所有测试文件分成 2 部分,并在这些 CI 实例中执行它们。
测试列表的前半部分将在第一个实例中执行,测试列表的后半部分将在第二个实例中执行。

使用编排器进行并行测试

可能会出现每个测试完成所需时间不同的情况,这可能导致 CI 构建之间的时间不均衡。为了缓解这个问题,你可以使用测试编排器,它根据 CI 实例的可用性来运行下一个测试。使用测试编排器进行并行测试的命令如下。

命令:

bench --site [sitename] --app [app-name] run-parallel-tests --use-orchestrator

用法:

如果你想在 2 个 CI 实例上运行测试,你的命令如下

# in first CI instance
bench --site [sitename] run-parallel-tests --use-orchestrator

# in second CI instance
bench --site [sitename] run-parallel-tests --use-orchestrator

注意:此命令需要环境变量 CI_BUILD_ID 和 ORCHESTRATOR_URL。CI_BUILD_ID 是你在每次 CI 构建运行时获得的唯一 ID。
ORCHESTRATOR_URL 是你托管编排器后获得的公开可访问 URL。

对比

为了清楚地了解上述并行测试命令变体的工作方式,请查看以下示例。

假设有 4 个测试文件如下

test_module_one.py 4 mins (execution time)
test_module_two.py 2 mins
test_module_three.py 1 min
test_module_four.py 1 min

不使用并行测试命令所需的时间。

test_module_one.py 4 mins
test_module_two.py 2 mins
test_module_three.py 1 min
test_module_four.py 1 min
==============================
Total Wait Time 8 mins

使用第一个命令将测试文件自动拆分到 2 个测试实例所需的时间。

# First instance # Second instance
test_module_one.py 4 mins test_module_three.py 1 min
test_module_two.py 2 mins test_module_four.py 1 min
---------------------------- ----------------------------
 6 mins 2 mins

==============================
Total Wait Time 6 mins

使用第二个命令(使用编排器根据 2 个测试实例的可用性运行测试)所需的时间可能如下。

# First instance # Second instance
test_module_one.py 4 mins test_module_two.py 2 mins
---------------------------- test_module_three.py 1 mins
 4 mins test_module_four.py 1 min
 ----------------------------
 4 mins
==============================
Total Wait Time 4 mins

注意:第一个实例上只执行了一个测试文件,因为它忙碌了 4 分钟。到那时,第二个实例能够执行其他测试文件,这有助于平衡构建之间的时间。

速率限制

Frappe 框架开箱即用地支持对 HTTP 请求进行速率限制。

Frappe 框架基于请求所消耗的时间实现了固定窗口速率限制。该限制针对在配置窗口内所有 HTTP 请求所耗时间之和进行约束。每经过 window 秒后周期会重置,例如,将 window 设置为 3600 秒,就会根据站点时区在每个小时开始时将使用计数器重置为 0。

注意:超过限制的请求不会被处理,并会返回 HTTP 429(请求过多)响应。

你可以通过在 site_config.json 中添加类似以下配置来为你的站点启用速率限制:

{
 "rate_limit": {
 "limit": 600,
 "window": 3600
 }
}
键 描述
limit 在速率限制窗口内允许使用的最大时间量(以秒为单位)。
window 速率限制窗口的大小(以秒为单位)。

每个 HTTP 请求返回的 HTTP 头都会显示当前的速率限制状态,例如:

curl -i https://frappe.io/docs
HTTP/1.1 200 OK
X-RateLimit-Limit: 600000000
X-RateLimit-Remaining: 518060453
X-RateLimit-Reset: 3513
X-RateLimit-Used: 100560

如果在配置的限制耗尽后仍有请求,则会返回 HTTP 429 响应以及速率限制状态:

curl -i https://frappe.io/docs
HTTP/1.1 429 TOO MANY REQUESTS
X-RateLimit-Limit: 600000000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1242
Retry-After: 1242
头部 描述
Retry-After 当前速率限制窗口重置前剩余的时间(以秒为单位)。
X-RateLimit-Limit 在一个速率限制窗口内允许使用的时间(以微秒为单位)。
X-RateLimit-Remaining 当前速率限制窗口中剩余(可使用)的时间(以微秒为单位)。
X-RateLimit-Reset 当前速率限制窗口重置前剩余的时间(以秒为单位)。
X-RateLimit-Used 处理当前请求所使用的时间(以微秒为单位)。

零停机迁移

零*停机迁移让站点在升级过程中以只读模式运行。这意味着你的静态站点、只需要读取数据的动态页面都可以继续正常工作,用户不会察觉到任何差异。任何需要写入数据库的操作都会被禁用,直到升级完成后才会恢复。

这是如何实现的

零停机迁移通过以下两种方式之一来启用只读模式:

  1. 使用只读副本。
  2. 使用数据库提供的“只读事务”。

无论采用哪种方式,在维护模式解除之前,数据库的写入操作都会被禁用。

在更新期间,网页端和 Desk 都会显示持续的只读警告。所有表单也会变为只读,因此你无法修改任何数据。

需要注意的事项

  • 并非所有应用都自动支持此功能。如果它们没有针对只读模式进行设计,可能会出现异常行为。
  • 在数据库架构更新期间,客户端应用也会被更新,因此你偶尔会遇到一些小问题。这些无法完全避免;此功能的目的仅仅是减少停机带来的影响。

在升级期间启用只读模式

  1. [可选] – 按照复制指南设置一个只读副本
  2. 编辑你的站点配置以启用此功能。 bench --site develop set-config allow_reads_during_maintenance 1

在你的应用中支持只读模式

如果你是一名应用开发者,并且想在你的应用中添加对此功能的支持,那么你需要修改你的应用

服务端更改

你可以使用 frappe.flags.read_only 来在只读模式下禁用或修改行为。

示例:记录页面访问

@frappe.whitelist()
def log_visit():
    if frappe.flags.read_only:
        return

    doc = frappe.new_doc("Doc Visit")
    doc.insert()

Jinja / 客户端页面更改

理想情况下,你应该更新 UI 以表明你的应用处于只读模式,当前无法执行写入操作。

网站上下文默认包含 read_only_mode 键;你可以利用它在页面上有条件地添加警告或切换功能。示例:

<!-- some html page -->
{% if read_only_mode %}
    <div class="alert">Site is running in read only mode. Full functionality will be restored soon.</div>
{% endif %}
<!-- other content -->

生产设置

Bench 是用于管理基于 Frappe 框架的站点部署的命令行工具。以下是在生产环境中部署 Frappe 站点的步骤。

安装 Bench

部署 Frappe 站点与在本地系统上搭建并没有太大区别。如果你的服务器是受支持的 Linux 发行版(Debian、Ubuntu、CentOS),可以使用 Easy Install 脚本安装 Bench。请确保向脚本传递 --production 标志。

sudo python install.py --production --user [frappe-user]

设置站点和应用

# change directory to frappe-bench
cd frappe-bench

# create a new site
bench new-site example.com

# download frappe apps or your custom-apps
bench get-app erpnext
bench get-app https://github.com/yourremote/yourapp.git

# install apps onto your site
bench --site example.com install-app erpnext yourapp

检查 supervisor

如果一切设置正确,你应该能在 supervisor 状态输出中看到 Frappe 进程。

$ sudo supervisorctl status
frappe-bench-redis:frappe-bench-redis-cache RUNNING pid 6467, uptime 10 days, 8:12:09
frappe-bench-redis:frappe-bench-redis-queue RUNNING pid 6466, uptime 10 days, 8:12:09
frappe-bench-redis:frappe-bench-redis-socketio RUNNING pid 6468, uptime 10 days, 8:12:09
frappe-bench-web:frappe-bench-frappe-web RUNNING pid 8856, uptime 10 days, 4:32:18
frappe-bench-web:frappe-bench-node-socketio RUNNING pid 8858, uptime 10 days, 4:32:18
frappe-bench-workers:frappe-bench-frappe-default-worker-0 RUNNING pid 8823, uptime 10 days, 4:32:19
frappe-bench-workers:frappe-bench-frappe-long-worker-0 RUNNING pid 8824, uptime 10 days, 4:32:19
frappe-bench-workers:frappe-bench-frappe-schedule RUNNING pid 8822, uptime 10 days, 4:32:19
frappe-bench-workers:frappe-bench-frappe-short-worker-0 RUNNING pid 8825, uptime 10 days, 4:32:19

如果你拥有 example.com 并且它已映射到服务器的 IP 地址,你的站点应该可以通过 example.com 访问。

更新

要更新你的站点,只需运行以下命令。它会更新你所有的应用(git pull),在所有站点上运行补丁,构建 JS 和 CSS 资源,并重启 supervisor。

# update everything
bench update

# update apps
bench update --pull

# run patches only
bench update --patch

# build assets only
bench update --build

# update bench (the cli)
bench update --bench

# update python packages and node_modules
bench update --requirements

Redis 命名空间

命名空间有助于保护用户数据。多个 Bench 可以共用同一个 Redis 实例,而无需担心命名空间冲突。你可以按照以下设置步骤在 Frappe 中启用 Redis 命名空间(目前 Frappe 在 Bench 级别支持 Redis 队列的命名空间)。

你可以使用 bench create-rq-users 命令行工具创建 Redis 用户,并使用凭据配置 Frappe。

bench create-rq-users

# Optional flags
# --use-rq-auth Enable Redis authentication for all bench sites
# --set-admin-password sets default user admin password

上述命令会在 Bench 配置目录中生成一个 ACL 文件。请确保 Redis_queue.conf 文件已配置为使用 ACL 文件(如果尚未配置的话)(或者,使用最新版本的 Bench CLI 运行 bench setup redis)。

aclfile [frappe-bench-absolute-path]/config/redis_queue.acl

数据库迁移

项目在开发过程中经常会遇到与数据库架构相关的变更。有时还需要对现有数据进行修补。Frappe 提供了迁移和补丁系统工具来处理这些情况。

当你的应用中有架构变更时,要将现有站点的数据库迁移到新架构,你应该运行以下命令。

bench --site [sitename] migrate

架构变更

你可以编辑 DocType 来添加、删除或修改字段。保存 DocType 时,包含 DocType 数据的 JSON 文件会被添加到应用的源代码树中。当你将应用添加到站点时,会使用这个 JSON 文件来安装 DocTypes(创建数据库表)。

要进行架构变更,你必须启用开发者模式。

运行 migrate 时,所有应用都会迁移到其当前版本。首先,我们运行 “before_migrate” 钩子来同步用户权限。然后运行实际的补丁,迁移各个应用。之后,我们同步以下组件:

  • 数据库架构
  • 后台任务
  • Fixtures(了解更多)
  • 仪表板、桌面图标和网页
  • 更新翻译
  • 为所有路由重建搜索索引

特别是对于 DocTypes,我们会将每个 DocType JSON 的 MD5 哈希值与存储在 DocType 数据库表中的哈希值进行比较。如果哈希值不匹配,我们会重新加载该 DocType。这种技术称为校验和比较,你可以在这里了解更多。最后,我们运行 “after_migrate” 钩子来完成迁移。你可以在 bench-site-commands 中找到各个进程运行的命令。

注意:在 v13 之前,DocTypes 是基于 JSON 文件中的修改时间戳来同步的。这种方法容易出错,所以我们改用了更可靠的哈希比较方法。你可以在这里了解更多。

当你删除或重命名 DocType 中的字段时,对应的数据库列不会从数据库表中删除,但它们不会在表单视图中显示。这样做是为了避免潜在的数据丢失情况,并允许你编写可能需要旧字段值的数据迁移(补丁)。

Frappe 不支持反向架构迁移。

数据迁移

在引入与数据相关的变更时,你可能需要运行一次性脚本来将现有数据更改为符合新代码预期的状态。在 Frappe 中,我们将这些脚本称为补丁。

编写补丁

要编写补丁,你必须在 Python 脚本中编写一个 execute 方法,并将其添加到应用的 patches.txt 中。

建议在文件路径中包含补丁编号和名称,并将其添加到应用的 patches 包(目录)中。然后你可以将补丁模块的点分路径添加到 patches.txt 中。

Frappe 中遵循的目录结构如下

frappe
└── patches
 └── v12_0
  └── my_awesome_patch.py

然后可以通过点分路径将补丁添加到 patches.txt 中。

frappe.patches.v12_0.my_awesome_patch

补丁执行期间的架构

execute 函数中可用的 DocType meta 将按照旧的 JSON 来提供。这样你就可以在编写迁移代码时假设仍然拥有旧字段。补丁运行完成后,新架构才会应用到 DocType。

如果你希望在补丁执行期间使用新架构,请使用 reload_doc 方法。

import frappe

def execute():
 frappe.reload_doc(module_name, "doctype", doctype_name)

 # your patch code here

模型同步后补丁 [仅限 v14 / develop 分支]

通常你的补丁可能不需要在 DocType 模型与数据库同步之前访问数据库架构。在这种情况下,最好将补丁放在 patches.txt 的 [post_model_sync] 部分中。

patches.txt 支持类似 INI 的文件格式,其中两个部分指定了补丁应该在何时运行——在 doctype 架构迁移之前或之后。模型同步后补丁不需要重新加载任何 doctypes,因为所有 doctypes 在执行它们之前都会被重新加载。以下是这种 patches.txt 文件的示例:

[pre_model_sync]
app.module.patch1
app.module.patch2

[post_model_sync]
app.module.patch3
app.module.patch4

一次性 Python 语句

你也可以使用以下语法在 patches.txt 中添加一次性 Python 语句,

frappe.patches.v12_0.my_awesome_patch
execute:frappe.delete_doc('Page', 'applications', ignore_missing=True)

补丁执行顺序

补丁按照定义的顺序运行。patches.txt 中的所有行必须是唯一的。如果某个补丁之前已经运行过,它就不会再次运行。如果你想再次运行某个补丁,可以添加一条注释,使该行看起来是新的。

例如,

frappe.patches.v12_0.my_awesome_patch #2019-09-08

基准流程文件

bench start 使用 honcho 在开发者模式下管理多个进程。

进程

运行 frappe 所需的各种进程包括:

  1. bench start – Web 服务器
  2. redis_cache 用于缓存(通用)
  3. redis_queue 用于管理后台工作进程的队列
  4. redis_socketio 作为消息代理,用于实时更新 / 来自后台工作进程的更新
  5. web 用于 frappe Web 服务器。
  6. socketio 用于实时消息传递。
  7. schedule 用于触发定时任务
  8. worker_* Redis 工作进程,用于处理异步任务

另外,如果你正在为 frappe 进行开发,可以添加:

bench watch 以自动构建 desk JavaScript 应用。

示例

 redis_cache: redis-server config/rediscache.conf
 redis_socketio: redis-server config/redissocketio.conf
 redis_queue: redis-server config/redisqueue.conf
 web: bench serve --port 8000
 socketio: /usr/bin/node apps/frappe/socketio.js
 watch: bench watch
 schedule: bench schedule
 worker_short: bench worker --queue short
 worker_long: bench worker --queue long
 worker_default: bench worker --queue default

基准测试命令速查表

可打印的 Bench CLI 速查表

在此处获取最重要的 Bench CLI 命令的 A4 大小可打印速查表 [这里](/files/Bench Cheatsheet v1.1.pdf)。下载、打印并放在你的桌子上!

常规用法

  • bench --version – 显示 bench 版本

  • bench version – 显示所有应用的版本

  • bench src – 显示 bench 仓库目录

  • bench --help – 显示所有命令和帮助

  • bench [command] --help – 显示命令的帮助

  • bench init [bench-name] – 创建一个新的 bench(从主目录运行)

    • --python TEXT Python 可执行文件的路径。
    • --ignore-exist 如果 Bench 实例已存在则忽略。
    • --apps_path TEXT 初始化后要安装的应用的 json 文件路径
    • --frappe-path TEXT frappe 仓库的路径
    • --frappe-branch TEXT 克隆 frappe 的特定分支
    • --clone-from TEXT 从路径复制仓库
    • --clone-without-update 从路径复制仓库但不更新
    • --no-procfile 拉取 bench 中所有应用的更改
    • --no-backups 为 bench 中的所有站点运行迁移
    • --no-auto-update 为 bench 构建 JS 和 CSS 产物
    • --skip-redis-config-generation 如果已经指定了 common-site-config 文件,则跳过 redis 配置生成
    • --verbose 安装期间输出详细日志
  • bench --site [site-name] COMMAND – 为命令指定站点

  • bench update – 拉取 bench-repo 和所有应用的更改,应用补丁,构建 JS 和 CSS,然后进行迁移。

    • --pull 拉取 bench 中所有应用的更改
    • --patch 为 bench 中的所有站点运行迁移
    • --build 为 bench 构建 JS 和 CSS 产物
    • --bench 更新 bench
    • --requirements 更新依赖项
    • --restart-supervisor 更新后重启 supervisor 进程
    • --no-backup 更新前不进行备份
    • --reset 忽略本地更改并更新
    • --dev 在新的 bench 上启用开发者模式,并安装运行测试所需的开发者依赖项。
  • bench restart 重启所有 bench 服务

  • bench backup 创建默认站点的备份。

备份将保存到 ~/frappe-bench/sites/{your site}/private/backups/。

  • bench backup-all-sites 备份所有站点

    • --with-files 备份包含文件的站点
  • bench restore 恢复

    • --with-private-files 恢复包含私有文件的站点(tar 文件路径)
    • --with-public-files 恢复包含公共文件的站点(tar 文件路径)
  • bench migrate 运行补丁、同步架构并重建文件/翻译

    • --rebuild-website TEXT 迁移后重建网页
  • bench migrate-env [python-version] 将虚拟环境迁移到所需的 Python 版本

  • bench --site [sitename] set-admin-password [password] 将重置管理员密码

  • bench destroy-all-sessions 销毁所有会话

配置

  • bench config – 更改 bench 配置

    • auto_update [on/off] 启用/禁用 bench 自动更新
    • dns_multitenant [on/off] 启用/禁用 DNS 多租户
    • http_timeout 设置 HTTP 超时
    • restart_supervisor_on_update 启用/禁用 supervisor 自动重启
    • serve_default_site 配置 nginx 以在以下地址提供默认站点
    • update_bench_on_update 启用/禁用运行 bench 时的 bench 更新…
  • bench setup – 设置组件

    • auto-update 为 bench 自动更新添加 cronjob

    • backups 为 bench 备份添加 cronjob

    • config 覆盖或创建 config.json

    • env 为 bench 设置 virtualenv

    • nginx 为 nginx 生成配置

    • procfile 为 bench start 设置 Procfile

    • production 将 bench 设置为生产环境

    • redis 为 redis 缓存生成配置

    • socketio 为 socketio 服务器设置 node 依赖项

    • sudoers 将命令添加到 sudoers 列表以执行…

    • supervisor 为 supervisor 生成配置

    • add-domain 为站点添加自定义域名

    • firewall 设置防火墙并阻止除 22、80 和 443 之外的所有端口

    • ssh-port 更改默认 SSH 连接端口

    • requirements 更新 Python 和 Node 包

      • --node 仅更新 Node 包
      • --python 仅更新 Python 包
      • --dev 仅更新 Python 开发依赖项。
  • bench set-nginx-port [sitename] [port] – 在生产环境中设置站点的端口

开发

  • bench new-app [app-name] 创建一个新应用

  • bench get-app [repo-link] – 从 git 仓库下载应用并安装

    • --branch [branch-name] 获取应用的特定分支
  • bench install-app [app-name] 安装现有应用

  • bench remove-from-installed-apps [app-name] 从应用列表中移除应用

  • bench uninstall-app [app-name] 删除应用及与应用链接的所有内容(Bench 需要正在运行)

  • bench remove-app [app-name] 从 bench 中完全移除应用

  • bench --site [sitename] --force reinstall 使用全新数据库重新安装(注意:将清除旧数据库)

  • bench new-site [sitename] – 创建一个新站点

    • --db-name 数据库名称
    • --db-host 用于远程连接的自定义数据库主机 URL
    • --db-port 自定义数据库监听端口(例如,非默认的 3306 端口)
    • --mariadb-root-username MariaDB 的 root 用户名
    • --mariadb-root-password MariaDB 的 root 密码
    • --admin-password 新站点的管理员密码
    • --verbose 详细输出
    • --force 如果站点/数据库已存在,则强制恢复
    • --source_sql 使用 SQL 文件初始化数据库
    • --install-app 安装后安装应用`
  • bench use [site] 设置默认站点

  • bench drop-site 从磁盘和数据库中彻底删除站点

    • --root-login
    • --root-password
  • bench set-config [key] [value] 向站点的配置文件添加键值对

  • bench console 在 bench 虚拟环境中打开 IPython 控制台

    • --autoreload 自动重新加载代码更改
  • bench execute 在任何应用内执行方法。

    • 例如:bench execute frappe.utils.scheduler.enqueue_scheduler_events
  • bench --site [sitename] mariadb 打开 SQL 控制台

  • bench --site [sitename] export-fixtures [--app TEXT] 将应用钩子中定义的记录导出为 JSON 文件。

  • bench run-tests 运行测试

    • --app 应用名称
    • --doctype 要运行测试的 DocType
    • --test 特定测试
    • --module 运行包含测试的特定模块
    • --profile 在测试上运行 Python 性能分析器
  • bench disable-production 禁用生产环境

  • bench serve 在 0.0.0.0 上启动开发 Web 服务器

    • --port 在不同端口上启动 Web 服务器(默认:8000)
    • --profile 添加一个性能分析中间件,打印出具体调用了哪些后端方法、调用频率以及耗时。
    • --proxy 开发服务器可以运行在代理之后,例如 ngrok / localtunnel
    • --noreload 当文件更改时,禁用服务器的自动重启。
    • --nothreading 禁用使用线程处理并发请求。当需要多线程时(例如在 PDF 生成期间),可能导致意外行为。
    • --with-coverage 测量代码覆盖率并在 sites/ 文件夹中创建报告 coverage.xml。同时激活 --noreload 和 --nothreading。

调度器

  • bench enable-scheduler – 启用将运行计划任务的调度器
  • bench doctor – 获取有关后台工作进程的诊断信息
  • bench show-pending-jobs– 获取待处理任务
  • bench purge-jobs – 销毁所有待处理任务

数据导入

  • bench data-import – 从 .csv、.xls 或 .xlsx 导入批量数据

    • --file 文件名
    • --doctype DocType
    • --type 插入/更新

后台服务

运行一个活跃的 Frappe 环境需要运行各种系统级服务以及一些 Frappe 进程。

系统级服务,除了你选择的数据库解决方案和 Redis 之外,还取决于你的部署架构和策略。至于 Frappe 服务,它们定义在开发 bench 的 Procfile 和生产 bench 的 config/supervisor.conf 中。

系统进程

这些进程在系统级别处理,可能通过 bench 或其他 Frappe 部署管理工具进行配置。

  • MariaDB/PostgreSQL(数据库)
  • Redis(后台工作进程的队列和缓存)
  • NGINX(生产部署的反向代理)
  • Supervisor(bench 非容器化生产部署的进程管理)

Frappe 进程

这些进程可以手动触发,也可以通过进程管理器触发。不过,它们定义在 Frappe 的代码库中,并由其管理。

  • WSGI 服务器

WSGI 服务器负责响应发送到 frappe 的 HTTP 请求。在开发场景中,bench serve 或 bench start 会启动 Werkzeug 服务器。对于生产环境,Bench 使用 Gunicorn(在 supervisor 中自动配置)。

  • Redis 工作进程

工作进程执行 Frappe 系统中的后台任务。

这些进程在运行 bench start 时自动启动,生产环境中则在 supervisor 配置中进行配置。

  • 调度器进程

调度器进程负责在 Frappe 系统中安排定时任务的入队。该进程在运行 bench start 时自动启动,生产环境中则在 supervisor 配置中进行配置。

设置生产

> 警告: 我们不建议在裸机服务器上安装。推荐的生产环境部署方式是使用 Docker 镜像。你可以在这里找到相关说明:
>
>

你可以通过配置 Supervisor 和 nginx 这两个程序来将 bench 设置为生产环境使用。如果你想把生产环境设置还原为开发环境设置,请参考以下命令

简易生产环境设置

如果你运行 sudo bench setup production,这些步骤会自动完成

手动生产环境设置

Supervisor

Supervisor 确保驱动 Frappe 系统的进程持续运行,并在它们意外崩溃时自动重启。你可以使用命令 bench setup supervisor 生成 Supervisor 所需的配置。配置将存放在 config/supervisor.conf 目录中。然后你可以将该文件复制或链接到 Supervisor 的配置目录,并重新加载使其生效。

例如,

bench setup supervisor
sudo ln -s `pwd`/config/supervisor.conf /etc/supervisor/conf.d/frappe-bench.conf

注意:

  • 如果你不需要生成启动 Redis 服务器的配置,请使用 --skip-redis 选项。例如 bench setup supervisor --skip-redis
  • 对于 CentOS 7,扩展名应为 ini,因此命令变为
bench setup supervisor
sudo ln -s `pwd`/config/supervisor.conf /etc/supervisor/conf.d/frappe-bench.ini #for CentOS 7 only

当你更新应用时,bench 还需要重启由 Supervisor 管理的进程。为了自动化这一操作,你需要使用命令 sudo bench setup sudoers $(whoami) 来设置 sudoers。

Nginx

Nginx 是一个 Web 服务器,我们用它来提供静态文件服务,并将其余请求代理到 Frappe。你可以使用命令 bench setup nginx 生成 nginx 所需的配置。配置将存放在 config/nginx.conf 文件中。然后你可以将该文件复制或链接到 nginx 的配置目录,并重新加载使其生效。

例如,

bench setup nginx
sudo ln -s `pwd`/config/nginx.conf /etc/nginx/conf.d/frappe-bench.conf

注意:当你更改配置后重启 nginx 时,如果你有另一个配置将服务器块设为端口 80 的默认配置(大多数情况下是 nginx 欢迎页面),可能会启动失败。你需要禁用该配置。它最可能存在的位置是 /etc/nginx/conf.d/default.conf 和 /etc/nginx/conf.d/default。在最新版本的 nginx 安装中,该文件位于 /etc/nginx/sites-available/default