第九章 动作(Actions)

篇别:第一篇 基础知识

本章学习目标

  • 区分 窗口动作、服务器动作、客户端动作、报表动作、URL 动作 的适用场景。
  • 能熟练配置 ir.actions.act_windowcontextdomainview_modetarget
  • 会使用 binding 将动作挂到列表/表单的 Action 菜单。
  • 理解 动作与菜单、按钮 的引用关系及常见调试方法。

导读:动作是「下一步去哪」

动作(Action) 描述用户或系统触发后 打开什么界面、执行什么逻辑、生成什么报表。菜单、按钮、doAction、定时任务等最终常归结为 返回或引用一条 ir.actions.* 记录


9.1 窗口动作(ir.actions.act_window)

9.1.1 知识要点

字段 作用
res_model 目标模型
view_mode list,form 顺序影响默认打开
view_id / search_view_id 指定具体视图记录(可选)
domain 预置过滤域
context 默认值、search_default_*、活跃公司等
target current / fullscreen / new(弹窗)
res_id 直接打开某条记录(少用)
limit 列表每页条数(若支持)

9.1.2 案例

案例 A:基础列表+表单

<record id="action_library_book" model="ir.actions.act_window">
  <field name="name">图书</field>
  <field name="res_model">library.book</field>
  <field name="view_mode">list,form,kanban</field>
  <field name="help" type="html">
    <p class="o_view_nocontent_smiling_face">创建第一本书</p>
  </field>
</record>

案例 B:带 domain 与 context

<field name="domain">[('state','!=','archived')]</field>
<field name="context">{
  'default_state': 'draft',
  'search_default_available': 1
}</field>

案例 C:弹窗向导

<field name="target">new</field>
<field name="view_mode">form</field>
<field name="res_model">library.loan.wizard</field>

9.1.3 截图占位

图 9-1 技术菜单中窗口动作

图 9-1b 同一动作在菜单打开后的 URL hash 片段

9.1.4 本节练习

  1. 实操:新建动作仅 kanban 模式打开图书。
  2. 简答context 中的 active_id 何时由框架注入?
  3. 判断domain 可被用户在 UI 完全移除后绕过。( )

参考答案提示:3. 对 UI 可改;敏感过滤须 记录规则方法内校验


9.2 服务器动作(ir.actions.server)

9.2.1 知识要点

  • 绑定模型model_id;代码中可操作 recordsenv(依「执行代码」类型)。
  • 用途:自动化批量标记创建活动触发向导;复杂逻辑仍建议 模型方法 + 服务器动作仅调用一行
  • 风险:Python 字符串存在 升级与环境漂移;生产需 权限与审计

9.2.2 案例

界面创建(描述性):模型 library.book,动作「执行 Python 代码」:

records.write({'state': 'archived'})

XML 定义(示意)

<record id="action_server_archive_books" model="ir.actions.server">
  <field name="name">批量归档</field>
  <field name="model_id" ref="model_library_book"/>
  <field name="state">code</field>
  <field name="code">
if records:
    records.write({'state': 'archived'})
  </field>
</record>

9.2.3 截图占位

图 9-2 服务器动作 Python 编辑器

9.2.4 本节练习

  1. 简答:服务器动作相对「菜单调 object 方法」的优缺点?
  2. 实操:创建服务器动作:给选中图书记录 message_post 一条内部备注(需 mail.thread)。
  3. 安全:谁可以编辑 ir.actions.server?如何限制?

参考答案提示:1. 低代码快 vs 难测难版本管理。3. 仅设置/技术管理员;可用组隔离。


9.3 客户端动作(ir.actions.client)

9.3.1 知识要点

  • tag:对应前端注册的 client action 名称。
  • 用于 仪表盘、全屏自定义页;需 assets 注册 JS 与 OWL 入口。

9.3.2 案例(思路)

  1. JS:registry.category("actions").add("my_dashboard", MyDashboardAction);
  2. XML:<field name="tag">my_dashboard</field>

详见第三十二章、第三十五章。

9.3.3 截图占位

图 9-3 客户端动作 tag

9.3.4 本节练习

  1. 拓展:在源码搜索 registry.category("actions"),记录一个内置 tag
  2. 简答:client action 与 act_window 打开 res_model 的差异?

9.4 报表动作(ir.actions.report)

9.4.1 知识要点

  • report_name:QWeb 模板名;report_typeqweb-pdfqweb-html 等。
  • paperformat_id:纸张;binding_model_id:绑定到模型的打印菜单。
  • 打印:用户界面「打印」触发;同样受 记录权限 约束。

9.4.2 案例

<record id="action_report_book_card" model="ir.actions.report">
  <field name="name">图书卡片</field>
  <field name="model">library.book</field>
  <field name="report_type">qweb-pdf</field>
  <field name="report_name">my_library.report_book_document</field>
  <field name="binding_model_id" ref="model_library_book"/>
  <field name="binding_type">report</field>
</record>

9.4.3 截图占位

图 9-4 打印下拉与 PDF

9.4.4 本节练习

  1. 实操:实现最小 QWeb PDF(可对接第十九章)。
  2. 简答binding_type=reportaction 的区别?

9.5 URL 动作(ir.actions.act_url)

9.5.1 知识要点

打开 外部或内部 URLtargetnew 新标签,self 当前窗口(易打断 SPA,慎用)。

9.5.2 案例

<record id="action_doc_odoo" model="ir.actions.act_url">
  <field name="name">Odoo 19 文档</field>
  <field name="url">https://www.odoo.com/documentation/19.0/developer.html</field>
  <field name="target">new</field>
</record>

内部深度链接(示例,以实际路由为准):

<field name="url">/web#action=123&amp;model=library.book&amp;view_type=list</field>

9.5.3 截图占位

图 9-5 新标签打开文档

9.5.4 本节练习

  1. 简答target=self 的典型用途与风险?
  2. 实操:菜单链到你们公司内部 Wiki(new)。

参考答案提示:1. 当前页跳转;风险是离开 WebClient 丢 SPA 状态。


9.6 动作绑定(Bindings)(新增)

9.6.1 知识要点

ir.actions.act_window 或报表上设置:

  • binding_model_id:挂到哪个模型。
  • binding_typeactionreport
  • binding_view_types:如 list,form;限制在哪些视图出现 Action 菜单。

9.6.2 案例

<record id="action_book_mass_tag" model="ir.actions.act_window">
  <field name="name">批量打标签</field>
  <field name="res_model">library.book.tag.wizard</field>
  <field name="view_mode">form</field>
  <field name="target">new</field>
  <field name="binding_model_id" ref="model_library_book"/>
  <field name="binding_view_types">list</field>
</record>

9.6.3 截图占位

图 9-6 列表「操作」中出现绑定项

9.6.4 本节练习

  1. 实操:绑定「导出选中 ISBN」向导(TransientModel)。
  2. 简答:binding 能否替代 ACL
  3. 判断:无列表权限的用户看不到 binding 动作,因此也绝对无法创建图书记录。( )

参考答案提示:2. 不能。3. 错,可能通过 API/其他入口。


9.7 动作链与返回值(Python)

9.7.1 知识要点

模型方法可 return

return {
    'type': 'ir.actions.act_window',
    'name': _('借阅单'),
    'res_model': 'library.loan',
    'view_mode': 'list,form',
    'domain': [('book_id', '=', self.id)],
    'context': {'default_book_id': self.id},
}

或使用 self.env.ref('...').read()[0] 合并修改。

9.7.2 案例

def action_view_loans(self):
    self.ensure_one()
    action = self.env['ir.actions.act_window']._for_xml_id('my_library.action_library_loan')
    action['domain'] = [('book_id', '=', self.id)]
    action['context'] = {'default_book_id': self.id}
    return action

9.7.3 本节练习

  1. 实操:Smart button 调上述方法打开过滤后的列表。
  2. 简答_for_xml_idref().read()[0] 的差异?

本章综合练习

  1. 画图:菜单点击 → ir.ui.menuact_windowir.ui.view 的数据流。
  2. 判断binding 可替代 ACL。( )
  3. 综合:设计「借阅超期」:服务器动作 发邮件 + 客户端动作 看仪表盘,各一条需求说明(不必全实现)。
  4. 调试:动作打开空白模型——列出检查项(res_model 拼写、模块安装、ACL、动作 id)。

参考答案提示:2. 错。


本章对应白皮书目录:第九章 动作。