第十章 菜单(Menus)
篇别:第一篇 基础知识
本章学习目标
- 能使用
<menuitem>构建多级菜单并正确绑定 action。 - 理解
parent、sequence、groups、web_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.4 本节练习
- 实操:实现 图书馆 → 图书 → 借阅统计 三级,第三级使用 独立 action(如带
domain/context)。 - 简答:
sequence相同的两项顺序是否确定? - 判断:父菜单没有
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.4 本节练习
- 简答:菜单限制
sales_team.group_sale_salesman,但library.book对group_user可读——风险?如何缓解? - 实操:用普通用户浏览器直接访问 带
action=的 URL,验证是否仍能打开视图。 - 情景:「隐藏菜单防竞争对手模块被发现」是否足够?还应做什么?
参考答案提示: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.4 本节练习
- 实操:导出模块翻译,修改一条菜单译名并导入验证。
- 简答:硬编码中文
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 本节练习
- 实操:故意删除父菜单 XML 再升级,记录报错并恢复。
- 简答:发现菜单指向 旧 action id 时,优先查什么表?
参考答案提示:2. ir.ui.menu 的 action 字段与 ir.model.data。
本章综合练习
- 设计:同一
action_library_book被 图书管理 与 快捷入口 两个菜单引用——利弊?何时应拆成两个act_window? - 国际化:列出你模块中所有 菜单 name,标出需翻译与无需翻译项。
- 安全:绘制简单示意图:菜单 groups → 动作 → 模型 ACL → 记录规则。
- 拓展:阅读官方「User interface / Menu」相关说明,记录
action字段存储格式(ir.actions.*,id)。
本章对应白皮书目录:第十章 菜单。