You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:57:39