Odoo @api.onchange执行后自定义字段未在表单界面更新问题
问题根源
你遇到的UI不更新问题是Odoo onchange机制的固有特性导致的,核心原因有3个:
@api.onchange触发时,你操作的是前端暂存在内存里的虚拟记录,不管是当前编辑的发票行,还是关联的父级发票单,此时都没有写入数据库。你代码里调用的rec.move_id.write()是直接修改数据库里的存量值,但当前打开的表单前端拿的是本地缓存的编辑态数据,不会主动重新读数据库,自然看不到变化。如果是还没点过保存的新建发票,move_id甚至是没有真实数据库ID的虚拟记录,这时候write调用根本不会生效。- 子表(One2many类型的发票行)触发的onchange,默认只会把当前行的字段变更返回给前端渲染,不会自动把父表(发票单)的字段变更通知给前端,哪怕你在内存里给父字段赋了值,前端收不到更新指令就不会刷新对应控件。
- 你同时写了
rec.move_id.global_discount=0(改内存缓存)和write(改数据库),两套操作数据源完全分离,很容易导致缓存和数据库值不一致,反而加重显示异常。
解决方法
修复onchange写法
注意:禁止在onchange方法中调用write、create这类直接操作数据库的方法,这类方法会绕过前端缓存直接改库,必然会导致界面显示和数据库数据不一致的问题。
onchange本身是保存前的预览交互逻辑,所有修改都应该在内存层面完成,等用户主动点保存时才会统一落库。修正后的代码如下:
from odoo import models, fields, api class SoftOneInvoiceLine(models.Model): _inherit = "account.move.line" change_from_user = fields.Boolean('Change by user', default=True) @api.onchange('quantity', 'discount', 'price_unit', 'tax_ids') def _onchange_price_subtotal(self): super()._onchange_price_subtotal() for rec in self: if rec.change_from_user: # 仅修改内存中父记录的缓存值,不要写库 rec.move_id.global_discount = 0 rec.change_from_user = True
如果改完还是不刷新,说明你用的Odoo版本前端one2many渲染器没有自动追踪父级字段变更,可以在onchange返回值里手动加更新标记,强制前端刷新父表单。
更稳妥的长期方案
不要用onchange实现这个逻辑,直接在account.move模型上把global_discount设为计算字段,依赖发票行的对应字段,Odoo的ORM会自动处理所有更新、同步、UI刷新逻辑,完全不会出现缓存不一致的问题,示例代码:
from odoo import models, fields, api class SoftOneInvoice(models.Model): _inherit = "account.move" global_discount = fields.Float( string='Global Discount', compute='_compute_global_discount', store=True, readonly=False # 允许用户手动修改 ) @api.depends('invoice_line_ids.quantity', 'invoice_line_ids.discount', 'invoice_line_ids.price_unit', 'invoice_line_ids.tax_ids') def _compute_global_discount(self): for move in self: # 这里可以加你需要的判断逻辑,配合change_from_user标记决定何时重置折扣 move.global_discount = 0
如果需要保留用户手动修改global_discount的能力,保留readonly=False参数即可。
内容的提问来源于stack exchange,提问作者Panagiotis Anagnostakis
相关产品推荐
相关产品推荐

