Frappe 站点创建测试用户流程
TL;DR
在远程 Frappe 站点通过 bench console 创建一个可登录的测试用户。关键难点:分步 commit 避免事务回滚、密码函数参数顺序、角色名必须存在于站点。
现状:✅ 已验证,测试完可一键删除。
🎯 背景:为什么需要这个
- 需求:SSO 测试需要一个真实可登录的 Frappe 用户来验证 OIDC 登录流程
- 卡点:不能动老师的管理员密码;Frappe
update_password()的参数顺序和调用方式容易踩坑;gottenergy 迁移站点的角色体系不是标准的System User - 约束:测试完必须能彻底删除用户,不留痕迹
✅ 前置条件
- 可 SSH 到 Press VPS (104.236.24.124)
- 通过 press-backend 容器可跳板到 bench 容器
- 知道目标站点的角色列表(
frappe.get_all("Role"))
🏗️ 最终架构
本地 → Press VPS → press-backend 容器 → SSH → n1/f1 VPS → bench console
为什么是三层跳板:Press VPS 的 press-backend 容器有 SSH key 可以免密访问 n1/f1。
🛠️ 部署步骤
第 1 步:进入目标站点 console
docker exec -i $(docker ps -qf "name=press-backend") ssh root@45.55.93.224 "echo '' | docker exec -i bench-0003-000007-f1 bench --site gottenergy.getgrowth.app console"这条命令在干嘛(点击展开)
-i不是-it——三层嵌套的 docker exec 不支持 TTY,用-i管道模式。echo '' |是为了让 console 收到一个空输入后退出,实际执行代码时替换echo ''为你的 Python 代码。
第 2 步:创建用户(分步 commit)
import frappe
from frappe.utils.password import update_password
frappe.init(site="gottenergy.getgrowth.app")
frappe.connect()
# 创建用户
user = frappe.get_doc({
"doctype": "User",
"email": "test@getgrowth.app",
"first_name": "Test",
"send_welcome_email": 0,
"enabled": 1
})
user.insert(ignore_permissions=True)
frappe.db.commit() # 先提交,防止后续失败回滚掉用户
# 重新加载
user.reload()
user.append("roles", {"role": "Desk User"})
user.save()
frappe.db.commit() # 单独提交角色
# 设密码
update_password("test@getgrowth.app", "test1234")
frappe.db.commit()
print("Created: test@getgrowth.app / test1234")这条命令在干嘛(点击展开)
三个必须分步 commit 的原因:1)
insert()后如果不 commit,后续update_password()失败会导致整个事务回滚,用户白创建了。2)reload()从数据库重新加载用户,确保后续操作基于最新状态。3)update_password(user, pwd)第一个参数是用户名(字符串),不是 User 对象。传错类型会触发TypeError: secret must be unicode or bytes。
第 3 步:确认密码生效
# 直接登录 https://gottenergy.getgrowth.app 验证↩️ 回滚 / 卸载
- 删除用户:
frappe.delete_doc("User", "test@getgrowth.app")然后frappe.db.commit() - 验证已删除:
frappe.db.exists("User", "test@getgrowth.app")应返回None - Auth 表残留:MariaDB
__Auth表会自动级联删除,不需要手动清理
🔍 验证方法
frappe.db.exists("User", "test@getgrowth.app")→ 不为 None- 浏览器打开
https://gottenergy.getgrowth.app→ 用 test 账号登录 → 成功进入 Frappe Desk
🧠 涉及到的代码逻辑
| 文件 | 关键方法/行号 | 做了什么 |
|---|---|---|
frappe/utils/password.py | update_password(user, pwd) L120 | 密码哈希写入 __Auth 表 |
frappe/model/document.py | Document.insert() | User Doctype 插入 |
frappe/model/document.py | Document.reload() | 从 DB 重新加载文档 |
frappe/core/doctype/user/user.py | User DocType 定义 | 用户字段与权限 |
流程简述:
get_doc创建 User 文档对象 →insert()写入tabUser表reload()从数据库读回 →append("roles")添加 Has Role 子表记录update_password()写入__Auth表(独立的认证表)- 登录时 Frappe 从
__Auth读取密码哈希做比对
💡 总结经验
- 这次最大的教训:
update_password()签名是(user, pwd)→ 用户名在前,密码在后。传 User 对象而不是字符串会报TypeError - 如果再来一次,我会先查
frappe.get_all("Role")确认角色存在,而不是假设System User存在 - 这个问题的本质其实是 迁移站点(gottenergy)的角色体系不是 Frappe 默认的,
System User是 Framework 自带角色但可能被 ERPNext 覆盖了
🕳️ 踩过的坑 & 被否决的方案
坑 1:update_password 参数顺序搞反
现象:
TypeError: secret must be unicode or bytes, not frappe.core.doctype.user.user.User根因:函数签名是update_password(user, pwd)——用户名(字符串)在前,密码在后。传了 User 对象作为第一个参数导致类型错误 解决:update_password("test@getgrowth.app", "test1234"),用字符串
坑 2:System User 角色不存在
现象:
LinkValidationError: Could not find row #4: Role: System User根因:gottenergy 是 frappecloud 迁移站点,角色体系不同,没有标准的 “System User” 角色 解决:先查frappe.get_all("Role")获取可用列表,用Desk User替代
坑 3:事务回滚导致用户创建白做
现象:用户创建成功(In[6] Out[6] 显示 User 对象),但后续
frappe.get_doc("User", "test@getgrowth.app")报 DoesNotExistError 根因:insert()成功后紧接着update_password()失败,同一个 commit 块内全部回滚 解决:分三步 commit——insert → commit → add role → commit → update_password → commit
🩺 故障排查决策树
症状:frappe.get_doc("User", "test@...") 报 DoesNotExistError
→ 用户没创建成功或已被回滚
→ 重新从 Step 2 执行,每步单独 commit
症状:登录提示 "登录无效"
→ 查密码是否设成功、角色是否添加
→ print(frappe.db.sql("SELECT password FROM __Auth WHERE name=%s", ("test@getgrowth.app",)))
→ 如果 password 列的 hash 以 $pbkdf2 开头 → 密码正常
→ 如果是 NULL 或空 → 重新跑 update_password
症状:登录成功但 OAuth 跳转后被拒
→ 查 OAuth Client 的 allowed_roles 是否包含用户的角色
🔒 安全 & 成本速查
- 测试用户用完即删:
frappe.delete_doc("User", "test@getgrowth.app") - 成本:$0
📌 待办 / 未来优化方向
- 写一个可复用的
create_test_user.py脚本,参数化站点名和角色名
📋 速查命令合集(复制专用,无解释)
docker exec -i $(docker ps -qf "name=press-backend") ssh root@45.55.93.224 "echo 'import frappe; from frappe.utils.password import update_password; frappe.init(site=\"gottenergy.getgrowth.app\"); frappe.connect(); user = frappe.get_doc({\"doctype\": \"User\", \"email\": \"test@getgrowth.app\", \"first_name\": \"Test\", \"send_welcome_email\": 0, \"enabled\": 1}); user.insert(ignore_permissions=True); frappe.db.commit(); user.reload(); user.append(\"roles\", {\"role\": \"Desk User\"}); user.save(); frappe.db.commit(); update_password(\"test@getgrowth.app\", \"test1234\"); frappe.db.commit(); print(\"done\")' | docker exec -i bench-0003-000007-f1 bench --site gottenergy.getgrowth.app console"
frappe.delete_doc("User", "test@getgrowth.app")
frappe.db.commit()📝 更新日志
| 日期 | 改动 | 原因 |
|---|---|---|
| 2026-07-09 | 首次创建,记录创建+删除测试用户完整流程 | SSO 测试需要 |