菜单可见性

Odoo 菜单为什么有时看得见却点不开:可见性过滤、action 预读和权限检查

菜单可见性不是单纯看 group_ids。Odoo 还会批量预读 action、检查模型读权限,并把祖先菜单一起补出来。

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

先说结论

Odoo 菜单是否显示,不是一个“有 group 就显示、没 group 就隐藏”的简单判断。

真正的链路是:

  • 先找出用户理论上能看的菜单
  • 再检查菜单挂的 action 是否存在
  • 再检查 action 对应模型有没有读权限
  • 最后把父级菜单一路补出来

所以有些菜单“理论上存在”,但用户实际看不到;也有些菜单看得到,却点进去才发现没有权限或动作失效。

_visible_menu_ids 在做什么

_visible_menu_ids() 是整个可见性判断的核心。

它先拿当前用户的 group 集合,再按这些 group 去查菜单。

这里有个很有意思的细节:

  • 如果不是 debug 模式,会把 base.group_no_one 排除掉
  • 查询菜单时会批量把 parent_idaction 一次读出来
  • 然后再根据 action 类型去检查对应模型是否存在

这意味着菜单可见性不是一次查一个,而是尽量批量处理。

为什么要先预读 action

菜单本身可见,不代表它挂的 action 还活着。

所以 Odoo 会先把 action 分类成不同模型:

  • ir.actions.act_window
  • ir.actions.report
  • ir.actions.server
  • 其他 reference 类型

然后分别检查它们是否存在。

对于窗口动作、报表动作、服务器动作,Odoo 还会顺手读取它们对应的目标模型字段,再做读权限校验。

这一步特别重要,因为:

一个菜单能不能点,不只看菜单本身,还看它背后的 action 能不能真正执行。

为什么祖先菜单也会被保留下来

如果某个子菜单可见,Odoo 会沿着 parent_id 一路把它的祖先也加入可见集合。

这是为了保证菜单树不会断掉。

否则你可能会看到一个“孤立子菜单”挂在一个不可见父节点下面,用户根本没法导航。

load_menus_root 和 load_menus 的作用

这两个方法负责把菜单树喂给前端。

  • load_menus_root():给根节点和顶层菜单
  • load_menus(debug):给完整菜单树,并结合黑名单和可见性过滤

其中 load_menus() 还会按 app 重新组织树结构,让前端拿到的是“用户真正能用的应用入口”。

常见误解

1)group_ids 空就是公开菜单

不完全是。即使菜单没有组限制,挂的 action 仍然可能因为模型权限而被挡掉。

2)菜单显示了就一定能进去

也不一定。action 可能存在,但目标模型没有读权限,最终还是会被过滤。

3)debug 模式和普通模式完全一样

不一样。debug 模式会保留 group_no_one 相关菜单,普通模式不会。

排查菜单“看不见”的顺序

如果一个菜单不显示,我会先看:

  1. 菜单的 group_ids
  2. 菜单挂的 action 是否存在
  3. action 对应模型是否能读
  4. 祖先菜单有没有被别的限制卡掉
  5. 是否处在 debug 模式

一句话总结

Odoo 菜单可见性是“菜单组权限 + action 存在性 + 目标模型读权限 + 祖先补全”的组合结果,不是单看菜单自己就能决定的。

DISCUSSION

评论区

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