如何在Odoo向导的One2many树形视图中创建可点击按钮?
解决Odoo Transient Model One2many行内按钮只读问题
这个问题确实挺影响用户体验的——Odoo默认要求记录必须有数据库ID才能触发按钮动作,而Transient Model又没有常规的保存按钮,导致未保存的One2many行按钮一直处于只读状态。我有两个实践验证过的方案,你可以根据场景选择:
方案1:自动创建父临时记录(最推荐)
Transient Model其实是会存储在数据库中的(只是Odoo会定期清理过期记录),所以我们可以让用户打开主向导时,自动生成一个临时的父记录。这样后续添加的One2many子行就能关联到这个有ID的父记录,子行也会被自动保存到数据库,按钮自然就可点击了。
代码实现:
父Transient模型:
from odoo import models, fields class ParentWizard(models.TransientModel): _name = 'parent.wizard' _description = 'Main Configuration Wizard' child_ids = fields.One2many('child.wizard', 'parent_id', string='Configuration Items') # 其他主向导配置字段... @classmethod def get_form_action(cls, env): """重载表单动作方法,自动创建临时父记录""" action = super().get_form_action(env) # 首次打开向导时(无active_id)自动创建临时记录 if not env.context.get('active_id'): temp_parent = cls.create({}) action['res_id'] = temp_parent.id return action
子Transient模型:
class ChildWizard(models.TransientModel): _name = 'child.wizard' _description = 'Configuration Item' parent_id = fields.Many2one('parent.wizard', required=True, ondelete='cascade') item_name = fields.Char(string='Item Name', required=True) # 其他子项配置字段... def open_sub_wizard(self): """子行按钮动作,此时记录已有ID,可正常执行""" return { 'name': 'Sub Configuration Wizard', 'type': 'ir.actions.act_window', 'res_model': 'sub.wizard', 'view_mode': 'form', 'target': 'new', 'context': { 'default_child_id': self.id, 'default_item_name': self.item_name } }
视图中的按钮配置:
无需额外设置只读属性,直接正常定义即可:
<tree editable="bottom"> <field name="item_name"/> <!-- 其他子项字段 --> <button name="open_sub_wizard" type="object" string="Configure" class="oe_highlight"/> </tree>
这个方案完全贴合Odoo原生机制,用户体验和常规模型一致,不需要额外的前端修改。
方案2:通过客户端动作直接读取前端数据
如果不想自动创建父记录,可以用JavaScript直接从前端读取未保存的行数据,再打开子向导并传递参数。这种方法适合不需要在服务器端处理子行数据,仅需传递参数的场景。
代码实现:
视图中的按钮配置:
<tree editable="bottom"> <field name="item_name"/> <!-- 其他子项字段 --> <button string="Configure" class="oe_highlight" onclick="openSubWizard(this)"/> </tree>
自定义JavaScript:
function openSubWizard(button) { // 获取当前行的DOM元素 const row = button.closest('tr'); // 读取行内字段值(根据实际字段类型调整选择器) const itemName = row.querySelector('.oe_field_widget[name="item_name"] input').value; // 构造子向导动作 const action = { name: 'Sub Configuration Wizard', type: 'ir.actions.act_window', res_model: 'sub.wizard', view_mode: 'form', target: 'new', context: { 'default_item_name': itemName } }; // 触发动作 odoo.action_manager.do_action(action); }
这种方法的缺点是需要手动处理不同字段类型的取值逻辑,若子向导需要依赖服务器端的子行数据,就不太适用了。
注意事项
- Transient Model的记录会被Odoo自动清理机制定期删除(默认每天一次),不用担心数据库冗余问题。
- 主向导的自定义确认按钮中,可以正常读取
self.child_ids的数据,因为子行都已保存到数据库。
内容的提问来源于stack exchange,提问作者forvas
相关产品推荐
相关产品推荐

