Odoo如何在非继承场景下实现跨模型字段依赖
@api.depends的方案不生效 首先要纠正对@api.depends跨模型能力的误解:这个装饰器的跨字段追踪,仅支持当前模型持有关联外键时,向上监听被关联模型的字段变化。
举个例子,你的自定义模型如果有个partner_id = fields.Many2one('res.partner')字段,写计算字段时依赖partner_id.x_float_1、partner_id.x_float_2是合法的,但这个触发逻辑有严格的前提:
只有当你在操作自定义模型记录、连带触发关联字段链路重算时,才会执行重算。如果用户单独打开res.partner的表单修改那两个Float字段,自定义模型的记录根本不会被触发重算——这就是你写了依赖但没效果的核心原因,装饰器从设计上就不负责监听独立模型的反向字段变更。
方案1:在res.partner侧重写write方法,主动反向同步字段
这是最稳定、性能可控的代码实现方式,核心逻辑是在res.partner的字段更新入口,检测需要同步的字段是否发生变化,反向找到所有关联该partner的自定义模型记录,批量更新对应值。
示例代码:
class ResPartnerExtension(models.Model): _inherit = 'res.partner' # 你新增的两个Float字段 x_sync_float_a = fields.Float(string='同步浮点值A') x_sync_float_b = fields.Float(string='同步浮点值B') def write(self, vals): # 先执行原生写入逻辑,保证原有流程不受影响 write_result = super().write(vals) # 定义需要同步的字段清单 SYNC_TARGET_FIELDS = ['x_sync_float_a', 'x_sync_float_b'] # 判断本次写入是否涉及需要同步的字段 need_sync = any(field in vals for field in SYNC_TARGET_FIELDS) if need_sync and self: # 收集所有需要同步的partner ID,避免循环查询造成N+1性能问题 partner_id_list = self.ids # 一次性查出所有关联这些partner的自定义模型记录 related_custom_records = self.env['your.custom.model.name'].search([ ('partner_id', 'in', partner_id_list) ]) # 遍历每个partner,给关联的自定义记录传对应的值(注意不要把partner的值错配) for partner in self: partner_linked_records = related_custom_records.filtered(lambda r: r.partner_id.id == partner.id) if partner_linked_records: sync_payload = { field: partner[field] for field in SYNC_TARGET_FIELDS } partner_linked_records.write(sync_payload) return write_result
这个方案的优势:
- 触发逻辑覆盖所有对res.partner的更新入口,不管是后台界面手动改、导入数据、接口对接修改,都能触发同步
- 可以灵活加异常判断、日志记录,适配复杂业务规则
- 只要自定义模型是通过
partner_id关联res.partner的,不管是一对一还是一对多关系都能兼容
方案2:用Odoo内置服务器自动化动作实现,无需改代码
如果不想直接改Python代码、需要快速上线,可以用系统自带的自动化能力实现联动:
- 进入「设置-技术-自动化-服务器动作」,新建动作
- 触发模型选择
res.partner,触发时机设为「当记录被更新时」,触发字段勾选你新增的两个Float字段 - 动作类型选择「更新相关记录」,关联规则设置为「查找你的自定义模型中,partner_id等于当前被修改partner的所有记录」
- 配置字段映射,把res.partner的两个Float字段值,同步到自定义模型的对应字段即可
这个方案适合临时需求、无代码环境的快速配置场景,缺点是复杂逻辑(比如加判断条件、异常通知)的灵活度比代码实现低。
常见误区提醒
很多技术资料提到的@api.depends跨模型依赖,从来没有设计成“跨模型反向监听独立字段变化触发计算”。它的所有触发逻辑,都严格围绕当前模型自身的字段访问链路展开,反向的跨模型同步从来不是这个装饰器的职责范围,不要在这上面继续绕弯路。
内容的提问来源于stack exchange,提问作者ITJ

