第十章 菜单(Menus)

篇别:第一篇 基础知识

本章学习目标

  • 能使用 <menuitem> 构建多级菜单并正确绑定 action
  • 理解 parentsequencegroupsweb_icon 等属性的作用。
  • 明确 菜单可见性 ≠ 数据安全 的边界。
  • 能对菜单做 国际化升级期冲突 的基本处理。

导读:菜单只是入口

ir.ui.menu 提供用户 可发现的导航入口;真正打开什么由 action 指向的 ir.actions.* 决定。没有菜单,用户仍可能通过 收藏夹、URL、其他模块按钮、RPC 触达动作——故 ACL 与记录规则 不可替代。


10.1 菜单层级

10.1.1 知识要点

  • <menuitem> 属性:
    • id:本模块内短名,完整 XML id = module.id
    • name:显示名(可翻译)。
    • parent:父菜单 完整 XML id 或本文件已定义 id。
    • sequence:同级排序,越小越靠前(常见约定)。
    • action:窗口动作等,写 ref 或 module.xml_id 字符串(视写法而定)。
    • active:默认 True;eval="False" 可隐藏。
  • 根菜单:常挂在 base.menu_custom,或放在 应用根application=True 模块的应用菜单下)。

10.1.2 案例

案例 A:三级结构

<menuitem id="menu_library_root" name="图书馆" sequence="10"
          web_icon="my_library,static/description/icon.png"/>

<menuitem id="menu_library_books" name="图书" parent="menu_library_root"
          action="my_library.action_library_book" sequence="10"/>

<menuitem id="menu_library_loans" name="借阅" parent="menu_library_root"
          action="my_library.action_library_loan" sequence="20"/>

<menuitem id="menu_library_report" name="借阅统计" parent="menu_library_books"
          action="my_library.action_library_loan_report" sequence="90"/>

案例 B:无 action 的「分组」父菜单

<menuitem id="menu_library_config" name="配置" parent="menu_library_root" sequence="100"/>
<menuitem id="menu_library_tags" name="标签" parent="menu_library_config"
          action="my_library.action_library_tag" sequence="10"/>

父级仅作 容器,可不绑 action;点击行为依主题可能为 展开无操作

案例 C:同一 action 挂两处

<menuitem id="menu_loans_alt" name="当前借阅" parent="menu_library_root"
          action="my_library.action_library_loan_open" sequence="15"/>

用于 不同上下文(另一 act_window 副本带不同 domain/context)比复用同一 id 更清晰。

10.1.3 截图占位

图 10-1 应用抽屉与侧栏菜单树

图 10-1b 技术菜单 → 菜单项:parent、action、sequence

10.1.4 本节练习

  1. 实操:实现 图书馆 → 图书 → 借阅统计 三级,第三级使用 独立 action(如带 domain/context)。
  2. 简答sequence 相同的两项顺序是否确定?
  3. 判断:父菜单没有 action 时,子菜单一定不可见。( )

参考答案提示:3. 错,子菜单仍可显示。


10.2 菜单与权限(新增)

10.2.1 知识要点

  • groups:仅 拥有所列组之一 的用户可见该菜单(具体逻辑以 ORM 为准,常见为 OR 关系)。
  • 不替代 ACL:无菜单 ≠ 无权限读模型。
  • web_icon:应用抽屉图标;路径 module,path 或类名(依版本)。
  • 调试:用 切换用户无痕窗口 验证菜单与 浏览器缓存

10.2.2 案例

<menuitem id="menu_library_admin" name="书库管理" parent="menu_library_root"
          groups="base.group_system"
          action="my_library.action_library_book_admin" sequence="90"/>

10.2.3 截图占位

图 10-2 普通用户与管理员菜单对比

10.2.4 本节练习

  1. 简答:菜单限制 sales_team.group_sale_salesman,但 library.bookgroup_user 可读——风险?如何缓解?
  2. 实操:用普通用户浏览器直接访问 action= 的 URL,验证是否仍能打开视图。
  3. 情景:「隐藏菜单防竞争对手模块被发现」是否足够?还应做什么?

参考答案提示:1. 仍可能从其他入口访问数据;加 记录规则独立模型。3. 不够;模块仍可能暴露于应用列表或技术菜单。


10.3 国际化与名称维护

10.3.1 知识要点

  • 菜单 name 可走 _() 翻译导出;XML 中常直接写源文,由 i18n 覆盖。
  • 模块改名 后注意 旧翻译条目 残留。
  • 多公司:菜单本身一般全局;数据隔离在 动作 domain / 规则

10.3.2 案例

<menuitem id="menu_library_root" name="Library" sequence="10"/>

i18n/zh_CN.po 中翻译 Library图书馆

10.3.3 截图占位

图 10-3 翻译界面中 ir.ui.menu 条目

10.3.4 本节练习

  1. 实操:导出模块翻译,修改一条菜单译名并导入验证。
  2. 简答:硬编码中文 name 与英文 name+翻译的利弊?

10.4 升级与冲突

10.4.1 知识要点

  • 重复 XML id:两模块定义 相同 module.name 的菜单会冲突。
  • 父菜单被删:子菜单升级失败;需 调整 parent依赖顺序
  • noupdate:若菜单数据 noupdate=1,升级可能 不更新 action 字段,导致「改了 XML 不生效」。

10.4.2 案例

升级前在测试库 grep 父 id

grep -R "menu_library_root" addons/my_library/

10.4.3 本节练习

  1. 实操:故意删除父菜单 XML 再升级,记录报错并恢复。
  2. 简答:发现菜单指向 旧 action id 时,优先查什么表?

参考答案提示:2. ir.ui.menuaction 字段与 ir.model.data


本章综合练习

  1. 设计:同一 action_library_book图书管理快捷入口 两个菜单引用——利弊?何时应拆成两个 act_window
  2. 国际化:列出你模块中所有 菜单 name,标出需翻译与无需翻译项。
  3. 安全:绘制简单示意图:菜单 groups → 动作 → 模型 ACL → 记录规则
  4. 拓展:阅读官方「User interface / Menu」相关说明,记录 action 字段存储格式(ir.actions.*,id)。

本章对应白皮书目录:第十章 菜单。