视图权限分层

Odoo 按钮为什么会忽隐忽现或直接不可点:groups、invisible、readonly 与 activeActions 的分工

很多人把按钮显示问题都归因于 view 继承或权限错配。可从 ir_ui_view 与 web 客户端源码看,Odoo 对按钮的处理至少分三层:服务端先按 groups 裁剪节点,再把 invisible/readonly 条件带到前端求值,最后还会受 create/edit/delete 这类 activeActions 影响。

Odoo 开发 前端
进阶 开发者 2 分钟阅读
0 评论 0 点赞 0 收藏 6 阅读

做 Odoo 界面调试时,按钮问题特别容易让人烦躁。

常见现象包括:

  • A 用户看得到按钮,B 用户看不到;
  • 同一个用户在列表页看得到,在表单里不见了;
  • 按钮明明显示出来了,却是灰的、点不动;
  • 改了一段 XML,以为是 XPath 没命中,结果根本不是继承问题。

这些现象之所以容易混乱,是因为很多人把“按钮能不能用”想成一个开关。

但源码视角下,它根本不是一个开关,而是几层机制叠在一起:

  1. 服务端按 groups 裁剪视图节点
  2. 前端按 invisible / readonly 表达式决定是否显示、是否可编辑
  3. 视图层 activeActions 再决定 create/edit/delete 等动作是否开放

所以更准确的说法是:

按钮不是“显示 or 不显示”二选一,而是先过服务端可见性,再过前端状态表达式,最后再过视图动作能力。

一、第一层:groups 在服务端就可能把节点删掉

ir_ui_view.py 里,_postprocess_access_rights() 做了一件特别关键的事:

  • groups 的节点,会先根据当前用户组匹配结果处理;
  • 如果用户不在允许组里,节点会直接从视图树里移除。

这一步的含义很重。

如果一个按钮是被 groups 挡掉的,那么对前端来说:

  • 它不是“有但隐藏”;
  • 而是压根就不存在

这也解释了为什么你有时打开浏览器调试,怎么都看不到那颗按钮:

不是 CSS 把它藏起来了,是后端返回的 arch 里就没这节点。

二、第二层:create / edit / delete 这类能力会写进视图动作能力里

同样在 _postprocess_access_rights() 里,Odoo 还会基于模型访问权限,给节点补充像下面这样的能力信息:

  • can_create
  • can_write

而在 Web 端,getActiveActions() 会把根视图节点上的:

  • create
  • edit
  • delete
  • duplicate

整理成 activeActions

这意味着就算按钮节点本身还在,用户是否能进行对应动作,仍然可能受视图整体动作开关影响。

例如列表页里:

  • 视图声明 edit="false"
  • 或者模型没有 write 权限
  • 或 action 本身限制了某些动作

都会让前端把这类操作当成不可用。

三、第三层:invisible / readonly 不是服务端删节点,而是前端表达式求值

在 Web 源码里,processButton() 会把按钮节点上的属性拆成:

  • clickParams
  • attrs
  • invisible
  • readonly

然后列表控制器、渲染器和 widget 会用 evaluateBooleanExpr() 去求这些表达式。

这表示:

groups

是后端结构裁剪。

invisible

通常是前端基于当前 record / context 计算:

  • 条件成立 → 不显示
  • 条件不成立 → 显示

readonly

则更像“显示了,但不能编辑 / 不能交互”。

这三者不是一回事。

四、为什么“明明按钮在 XML 里,却页面上没了”

最常见有三类原因。

1)被 groups 在服务端删掉

这种情况最彻底。

前端收不到节点,所以你会以为:

  • 继承没生效;
  • XPath 没打中;
  • 模块没升级;

其实只是当前用户组不匹配。

2)invisible 在前端求值为真

这时按钮通常还在 arch 语义里,但渲染层不显示。

列表页里就能看到类似:

  • t-if="!evalViewModifier(button.invisible)"
  • 或针对 record 的 evalInvisible(...)

也就是说,它不是永久没了,而是当前上下文下不显示

3)视图动作能力把它变成不可操作

有些按钮虽然看得到,但最终不能点,常见原因是:

  • 缺少 name/type/special 等必要 click 参数;
  • activeActions 不允许相应动作;
  • 按钮被标记为 disabled;
  • 对应记录处于只读态。

这时问题就不是“可见性”,而是“可执行性”。

五、缓存为什么会让你误判

ir_ui_view.py 里还有 _get_view_cache_key()

默认 cache key 会考虑:

  • view_id
  • view_type
  • mobile
  • lang
  • 某些 *_view_ref 上下文

这说明视图结果本来就会缓存。

而 skill 里也强调过一个重要边界:

  • _get_view_cache 缓存的是针对所有组的后处理视图
  • 之后仍要再做一次 group restricted block 的移除。

所以调按钮问题时,你不能只问“缓存有没有刷新”,还得问:

我看到的是缓存的通用视图,还是按当前用户组进一步裁剪后的最终视图?

六、现代 Odoo 里别再把 attrs / states 当万能解释

当前源码里甚至会校验旧式 attrs / states 的使用问题。

这意味着在较新的 Odoo 版本里,很多开发者脑中的老经验已经不完全适用。

更稳的理解方式是:

  • groups:服务端节点可见性
  • invisible / readonly 等表达式:前端基于上下文和记录值求值
  • activeActions:视图级动作能力边界

如果你还把所有按钮异常都归因于旧式 attrs/states,很容易看错方向。

七、实战排错时最有效的顺序

如果用户说:

为什么这个按钮有人有、有人没有、有人能看不能点?

建议按这个顺序查:

  1. 按钮节点有没有 groups
  2. 当前用户是否命中这些组
  3. 返回的最终 arch 里,这个节点还在不在
  4. 按钮的 invisible / readonly 表达式是什么
  5. 当前 record / context 求值后结果是什么
  6. 当前视图 activeActions 里对应动作是否允许
  7. 按钮本身是否缺少 name / type / special,导致 ViewButton.disabled 为真

这个顺序特别重要,因为它对应的就是源码真正运行的层次。

八、最容易产生的 4 个误判

误判 1:看不到按钮 = XPath 失败

不一定。

也可能是 groups 已经把节点删掉了。

误判 2:按钮显示了 = 用户一定能操作

不一定。

显示和可执行是两套判断。

误判 3:只要有权限,按钮就一定出现

也不一定。

还有 invisible 表达式、上下文条件、视图动作能力等因素。

误判 4:这一定是前端 CSS 问题

大多数时候不是。

Odoo 的按钮状态更多是数据驱动的结构与表达式结果,不是样式层的小毛病。

九、一句话记住这件事

Odoo 按钮的真实判定顺序,更接近于:

  • 先看后端是否允许这个节点存在(groups
  • 再看前端当前上下文下是否应该显示(invisible
  • 再看是否处于只读或禁用态(readonly / disabled)
  • 再看视图动作能力是否允许执行(activeActions

所以“按钮不见了 / 按钮灰了 / 按钮点不动”虽然看起来像一类问题,源码里其实属于三种不同层面的故障。

把这三层分开想,按钮类问题会一下子好排很多。

DISCUSSION

评论区

想参与讨论?先 登录 再发表评论。
还没有评论,你可以成为第一个留言的人。