第十七章 SmartButton 设计
篇别:第三篇 数据库与数据管理
本章学习目标
- 能使用 计算字段 + statinfo 实现 统计型 Smart Button。
- 能对 多记录批量统计 使用
read_group优化,避免 N 次search_count。 - 能配置
type="action"跳转并携带 domain、context、group_by。 - 能权衡 store=True 与 实时计算 对性能、搜索、缓存的影响。
导读:Smart Button 的用户价值
表单右上角 按钮盒(button box) 中的 统计按钮 让用户 一眼看到关联数量 并 一键跳入过滤后的列表/看板。实现上 = 展示用计算字段 + 窗口动作 + 可选图标与样式。
17.1 关联计数
17.1.1 知识要点
- 计算字段:
Integer,compute中search_count或 聚合查询;常store=False保证实时性,或store=True降低打开表单频率。 - 模板:
<div class="oe_button_box">内<button type="action">+<field widget="statinfo"/>。 - 性能:单张表单打开时,若对 多个 stat 各
search_count,会多次 SQL;可合并read_group或 单 SQL。
17.1.2 案例
案例 A:基础写法(易读,量大时慎用)
loan_count = fields.Integer(compute='_compute_loan_count', string='借阅次数')
@api.depends('partner_id')
def _compute_loan_count(self):
Loan = self.env['library.loan']
for partner in self:
partner.loan_count = Loan.search_count([('partner_id', '=', partner.id)])
案例 B:批量 read_group(多 partner 一次查询)
@api.depends('partner_id')
def _compute_loan_count(self):
partners = self.filtered('id')
if not partners:
return
Loan = self.env['library.loan']
data = Loan.read_group(
[('partner_id', 'in', partners.ids)],
['partner_id'],
['partner_id'],
)
count_by_pid = {}
for d in data:
pid = d['partner_id'][0]
# 计数字段名因版本而异,常见为 __count 或 partner_id_count
count_by_pid[pid] = d.get('__count', d.get('partner_id_count', 0))
for p in self:
p.loan_count = count_by_pid.get(p.id, 0)
字段名 partner_id_count 以 read_group 返回为准,不同 Odoo 版本可能为 __count 等,需打印 data 调试。
17.1.3 截图占位


17.1.4 本节练习
- 实操:将案例 A 改为案例 B 思路(打印
read_group结果校对)。 - 简答:
read_group返回条数上限与 分页 注意点? - 判断:
store=True的计算字段一定比store=False快。( )
参考答案提示:3. 错,依赖重算成本与索引。
17.2 跳转动作
17.2.1 知识要点
type="action",name使用%(module.xml_id)d引用ir.actions.act_window。- 上下文:
default_partner_id、search_default_*、active_id。 - Domain:也可在 Python 返回的 dict 里动态修改
action(Smart button 调object方法时常用)。
17.2.2 案例
案例 A:XML 静态绑定
<div class="oe_button_box" name="button_box">
<button type="action" name="%(library.action_loan_from_partner)d"
class="oe_stat_button" icon="fa-book">
<field name="loan_count" widget="statinfo" string="借阅"/>
</button>
</div>
预置动作 action_loan_from_partner 中:
<field name="domain">[('partner_id','=',active_id)]</field>
<field name="context">{'default_partner_id': active_id, 'search_default_open': 1}</field>
注意:active_id 在客户端求值;若动作仅用于 smart button,可写死 domain 用 Python 方法返回更安全。
案例 B:Python 返回动作(动态 domain)
def action_view_loans(self):
self.ensure_one()
action = self.env['ir.actions.act_window']._for_xml_id('library.action_library_loan')
action['domain'] = [('partner_id', '=', self.id)]
action['context'] = {'default_partner_id': self.id, 'group_by': 'state'}
return action
表单按钮:type="object", name="action_view_loans"。
17.2.3 截图占位

17.2.4 本节练习
- 实操:跳转列表默认 按
state分组(context或action中group_by)。 - 简答:
type="action"与type="object"返回 action 的选型? - 情景:计数为 0 时是否隐藏按钮?如何实现?
参考答案提示:2. XML 简单动作用 action;复杂 domain 用 object。3. invisible 表达式或 attrs。
17.3 图书场景:双统计按钮设计
17.3.1 知识要点
- 当前借出:
domain=[('state','=','open')] - 历史借阅:全部或
state in ('returned','lost') - 两个 compute 字段或 一个 read_group 多度量(视模型设计而定)。
17.3.2 本节练习
- 实操:在
library.book上增加「在借人数」「累计借阅次数」两个 stat(定义清晰业务含义)。 - 简答:「在借人数」应对 借阅行 去重用户还是计数行?
本章综合练习
- 设计:图书表单:当前借出册数(未还)+ 历史借阅次数;画出字段与关系。
- 性能:对比
store=Truestat 与read_group每次打开 的适用场景各三例。 - 无障碍:stat 按钮仅图标时,如何改善 屏幕阅读器 体验(简答)?
- 拓展:阅读
sale.order中 smart button 实现,摘录一种_for_xml_id用法。
本章对应白皮书目录:第十七章 SmartButton 设计。