先说结论
ir.embedded.actions 不是“放在某个模型上的普通动作”。它更像是:
只有在当前上下文确实指向某条记录时,才可能出现的上下文动作。
所以你在列表里看不到它,并不奇怪。它本来就不是给“空上下文”准备的。
create() 做了什么
创建时,模型会做两件很实用的事:
- 如果没传
name,就从action_id自动取名字 - 如果同时传了
action_id和python_method,会根据值真假保留真正生效的一项
这说明嵌入式动作的设计目标不是“数据表存一条记录”,而是“让动作定义尽量可配置、可复用”。
可见性判断的核心:active_id
_compute_is_visible() 一开始就先看 active_id。
如果上下文里没有 active_id,它直接判定不可见。
这一步很关键,因为后面的判断都建立在“当前有没有一个正在操作的目标记录”之上。
它到底在比对什么
可见性判断大致会同时看这些条件:
parent_res_model是否匹配当前 active 模型parent_res_id是否为空,或等于当前active_iduser_id是否为空,或等于当前用户groups_ids是否允许当前用户python_method是否真的存在于目标模型上domain是否能让 active 记录通过过滤
也就是说,嵌入式动作并不是单一权限判断,而是上下文 + 记录归属 + 用户归属 + 规则过滤的组合。
为什么 domain 是针对 active record,而不是动作本身
很多人第一次看这里会误会:domain 好像是在过滤动作记录。
其实不是。
它是拿来过滤“当前激活的那条业务记录”的。
所以这段逻辑更像:
- 先找到当前页面正在看的那条记录
- 再判断这条记录是否满足嵌入式动作的可用条件
这也是为什么嵌入式动作特别适合“只对某些单据状态显示”的按钮或快捷入口。
为什么没有 active_id 时一律隐藏
这是设计边界,不是 bug。
嵌入式动作的前提就是“有一个明确的当前记录”。
没有 active_id 时,系统根本没法安全判断:
- 这个动作应该挂到哪条记录
- 是否属于当前用户
- domain 是否应该通过
所以直接隐藏,比猜测更安全。
常见踩坑
1)父模型写错
parent_res_model 一旦不对,后面的判断全都对不上。
2)domain 写在了动作对象上,但实际想过滤别的记录
这里的 domain 只作用在当前 active 记录上。作用对象搞错,动作就永远不显示。
3)以为 user_id 和 groups_ids 任意一个满足就行
不是。它们是叠加关系,不是替代关系。
4)python_method 名字存在拼写错误
代码里会检查这个方法是否真的存在。名字写错,动作就直接变成不可见。
实战排查顺序
如果某个嵌入式动作不显示,我会先看:
- 当前页面有没有
active_id parent_res_model和parent_res_id是否匹配user_id/groups_ids是否挡住了当前用户domain是否把 active 记录过滤掉了python_method是否真正在模型上存在
一句话总结
嵌入式动作不是“挂上去就显示”,而是“当前记录、当前用户、当前规则都对上了才显示”。理解这一点,调试起来会快很多。
DISCUSSION
评论区