简介
在开始 Odoo 开发时,会不断遇到一个核心概念:继承。Odoo 的对象关系映射(ORM)允许模型之间互相扩展与复用字段,从而避免重复存储和冗余代码,这是平台灵活性的基石。
在这套机制里,所谓“继承字段”就是指某个模型并未在自身表里实际定义该字段,但可以像本地字段一样读写另一个模型上的字段。一旦掌握了这个思路,Odoo 的数据关联与统一性就能迎刃而解。
本文将带你从概念到实践:解释继承字段是什么、产生它的三种继承方式、这些字段在数据库中的表现,以及如何在真实定制项目中正确使用它们。
什么是 Odoo 中的继承字段?
在 Odoo 的 ORM 里,当一个模型通过特定的继承机制获得另一个模型的字段访问权时,这些字段就被视为“继承”的。子模型不需要重新声明字段,而是直接复用父模型已存在的定义和数据。
这正是 Odoo 数据模型保持简洁与一致性的原因:例如客户(partner)的同一个 name 字段,可以在销售单、CRM 线索和发票上出现,但它们最终读取的都是同一个来源记录。
Odoo 中的三种继承方式
Odoo 支持三类模型继承:每种方式对字段的处理与数据存储方式都有所不同,需要根据业务场景有意识地选择。
1. 经典继承(Classical Inheritance)
这是最常见的扩展方式:通过在模块里使用 _inherit 而不定义新的 _name,对现有模型直接追加字段或方法。数据库层面不会新增表,新增字段会存放到原模型对应的表中。
这也是大部分模块向标准模型添加字段的做法。例如 CRM 安装后会通过经典继承向 res.partner 增加一些 CRM 所需字段,这些字段与核心字段存放在同一个 partner 表里。
2. 原型继承(Prototype Inheritance)
当同时使用 _inherit 和 _name 时,会创建一个新的模型表,但初始结构来自父模型。父模型的字段被复制到子模型的新表中;此后对子模型的改动不会影响父模型。
这个模式在普通微调中较少用到,但适合需要基于既有结构创建独立数据集的场景。
3. 委托继承(Delegation Inheritance)
这是最具特点的一种,由 _inherits 定义。子模型通过一个 Many2one 指向父模型,并将父模型字段“按需”在子模型上公开。真正的数据仍存放在父模型的表中,而子模型仅通过关联访问这些字段。
严格意义上的“继承字段”通常指的就是这种情形:字段不被复制,读写操作会通过关联到父记录进行。
标准 Odoo 中一个典型例子是 res.users 与 res.partner 的关系:每个用户同时对应一个联系人,用户的姓名、邮箱与电话等信息实际上存在 partner 记录上,用户通过委托继承来访问这些数据。
字段的运作原理
委托继承的底层机制
采用 _inherits 时,ORM 在两个数据库表之间建立了一道透明的桥梁。读取子模型上的继承字段时,Odoo 会自动沿着 Many2one 链定位到父记录并返回父表的值。
写操作同样会被路由到父记录上。因此对开发者而言字段像是本地的,但数据库层面数据归属于父表。
由此带来的重要结论是:不会发生数据重复。更新某个 partner 的姓名后,所有引用该 partner 的地方(包括通过委托继承的用户记录)会立刻反映出相同的变更。
经典继承与数据库布局
采用经典继承时,新增字段直接添加到原模型所在的数据库表中,不会产生第二张表。数据库层面,扩展后的模型与原模型就是同一张表,这是在 Odoo 定制里最清晰也最常用的字段扩展方式。
related 字段:轻量级的“继承”方案
related 字段是一类特殊的计算字段,通过一段关系路径从关联记录读取值。它并不是真正改变模型结构的继承,但经常用于在不修改模型表结构的前提下,把别处的数据呈现在当前模型上。
比如在销售订单上定义一个 partner_country_id,路径指向 partner_id.country_id。该字段看起来像订单自身的字段,但值来源于对应的 partner。
related 字段可以设置 store=True 把计算结果持久化到数据库,这有助于加速搜索与过滤,但会增加存储成本,并在源字段变更时触发重新计算。
运行时字段解析流程
Odoo 在加载模型定义时会解析出完整的字段映射表,包括所有继承和 related 字段。启动后这些解析结果会被缓存——所以模型在可用之前,系统已确定每个字段的真正来源(本表、父表或关联链)。
典型业务场景
继承字段不仅是技术细节,还是解决业务一致性的工具。它们有助于在不重复数据的前提下,让系统不同模块共享同一份信息。
1. CRM 与销售:联系人信息统一管理
销售人员在 CRM 中创建线索或报价时,客户的姓名、电话和邮箱往往通过 partner 的 Many2one 关联直接读取。若联系人信息更新,所有相关的线索、报价和订单都会即时同步显示最新数据。
在这里,经典继承与 related 字段常常协作:CRM 会向 res.partner 增加与销售流程相关的字段(如线索计数、阶段等),同时销售单通过 related 字段把联系人信息显现出来。
2. 产品与变体管理
Odoo 通过委托继承来优雅地处理产品模板与具体变体的关系:product.product(变体)通过 _inherits 连接到 product.template(模板)。模板上保存通用字段(名称、类别、描述、销售价),变体记录只保存针对该变体特有的属性(如条形码或内码)。
这种设计让你可以为同款衬衫创建几十种颜色变体,而不必把名称与描述重复存储几十次。
3. 用户与联系人同步
res.users 使用 _inherits={'res.partner':'partner_id'},因此用户自动继承联系人所有字段(姓名、邮箱、电话、地址、头像等)。更新员工邮箱会同时反映在用户设置与联系人记录中。
4. 库存:移动记录显示产品信息
库存模块中的拣货、移动记录会显示来自产品模板的描述信息,这些信息通过关联链或 related 字段取得,使仓库人员在拣货单上即可看到最新的产品说明,无需在库存模块重复维护产品数据。
5. 会计:发票行与产品/客户信息一致性
会计凭证行引用产品和客户时,展示的产品名、会计科目和税务设置都来自产品或客户模型的原始配置。财务人员看到的数据与销售团队在产品上配置的一致,因为它们读取的是同一个来源。
如何创建或定制继承字段
使用 Odoo Studio 的无代码定制
Odoo Studio 是面向业务用户的无代码定制工具,能在界面上添加字段和调整视图。Studio 在后端实际上采用的是经典继承:它把新字段写入目标模型定义并在数据库中创建字段。
Studio 也支持创建 related 字段。当你在字段类型中选择“Related Field”时,可以沿任意可达的关系链指向源字段,比如在销售订单上直接读取客户的国家或税号。
对于多数业务顾问与功能性需求来说,Studio 是增加字段与视图的推荐工具:它会自动处理字段命名、数据库迁移和视图布局。
开发者的 Python 定制方式
对于开发者而言,在 Python 模块里用 models.Model 定义继承字段是最常见的做法。不同继承类型在代码中有不同的表现形式。
经典继承示例:向现有模型加入自定义字段。
示例代码展示了如何通过 _inherit 向 crm.lead 增加一个 x_contract_value(估算合同金额)字段,这种方式把字段直接加入到 crm.lead 的表结构中。
委托继承示例:创建新模型并共享父模型字段。
示例代码展示了如何用 _inherits 把 hr.employee.profile 与 res.partner 关联,profile 会通过 partner_id 访问并使用 partner 的字段,同时在自己的表里保存特有字段(如 employee_id)。
related 字段示例:从关联记录中读取值并在当前模型展现。
示例代码说明如何在 sale.order 上定义 partner_country_id,通过 related 路径读取 partner_id.country_id 并可选择存储(store=True)。
通过 XML-RPC API 远程配置字段
字段也可以通过 XML-RPC 和 ir.model.fields 接口以程序化方式创建。这是 Dasolo 在远程配置场景中常用的方法,与 Studio 的结果等效,适合在无需直接服务器控制台的情况下批量部署字段。
通过 API 添加 related 字段时,需要在 ir.model.fields 记录里指定合适的 ttype(如 many2one)并配置 related 路径。这种方式适合把字段创建纳入自动化部署流程。
最佳实践建议
为不同任务选择合适的继承方式
当只是向已有模型追加字段或方法时,优先使用经典继承:它简单明了,是大部分模块的首选。仅在确实需要独立记录集同时共享父模型结构(例如业务实体扩展联系人但又不是联系人本身)时,才考虑使用委托继承。
仅需展示只读数据时优先 related 字段
如果只是想在表单或列表上显示来自关联记录的值,用 related 字段比委托继承更轻量。它避免引入额外的结构性依赖,便于理解和维护。
在使用 store=True 时要谨慎
把 related 字段持久化能提升查询效率,但这意味着在数据库里生成一份拷贝,并在源字段变更时触发重新计算。只有在确实需要对该字段做大规模过滤或排序时才考虑存储。
自定义字段请用 x_ 前缀
向标准模型添加的自定义字段应以 x_ 开头(或在模块中用命名空间),以避免与将来 Odoo 正式版本新增字段产生冲突。
记录继承链与设计意图
在复杂定制中,最好在代码或文档里注明字段来自何处以及为何这样设计。例如 sale.order 上的 x_country_code 字段背后可能是 partner_id.country_id.code,注释能帮助未来维护者快速理解来源。
正确处理级联删除规则
在委托继承中,父记录通常应随子记录一起创建并在子记录删除时级联删除。为 _inherits 中的 Many2one 设置 ondelete='cascade' 能防止出现孤立的父记录。
常见易踩坑点
混淆三种继承方式的用法
对新手来说最常见的错误是当想做经典扩展时却同时写了 _inherit 和 _name,从而非预期地创建了一个全新模型。若不打算新建模型,切勿定义 _name。
在 _inherits 场景下忘记创建父记录
使用委托继承时,在通过 API 或脚本创建子记录的某些情况下父记录不会自动就位。通常 ORM 在常规 create 时会帮你创建父记录,但在手动构造数据或批量脚本里,你需要确保父记录先存在或由 ORM 正确触发,否则会遇到数据库约束错误。
试图通过继承改变字段类型
Odoo 不允许通过继承改变既有字段的类型。你可以修改字段的标签、domain 或帮助文本,但不能把 Char 改成 Integer 等。这样做会在模块安装时报错。
滥用已存储的 related 字段
为所有 related 字段一律加上 store=True 看似能提升性能,但会迅速增大数据库体积并带来维护成本。若源字段频繁变化,持久化的 related 字段会频繁触发重算,务必有选择地使用。
误以为继承字段会受子模型访问规则保护
通过委托继承获得的字段实际上物理存在于父模型上,父模型的访问控制会生效。如果只在子模型层面设置权限而忽视父模型,用户可能仍然通过父模型直接读到本应受限的字段值。使用委托继承时请同时审查父子模型的安全规则。
结语
继承字段并非 Odoo 的冷门特性,而是系统架构的一部分。用户与联系人、产品模板与变体、销售单与客户信息之间的这些关系,都是靠继承字段来保证数据一致性并避免重复存储的。
弄清楚何时用哪种继承、何时用 related 字段,以及这些机制在数据库层的实际表现,会让你成为更高效的 Odoo 定制工程师:写出更简洁的代码、设计更合理的数据模型,并少花时间排查字段异常行为。
核心结论很直接:Odoo 的 ORM 设计理念是让字段只在一个地方保存、却能被多个位置访问。继承字段正是支撑这一理念的机制,它帮助系统在模块间扩展时保持数据一致与可维护性。
需要我们帮助实施 Odoo 吗?
在 Dasolo,我们帮助企业实施、定制与优化 Odoo。无论是从零开发模块、在标准模型上添加自定义字段,还是排查数据模型异常,我们的团队都拥有丰富的实战经验,能帮你加速交付并避免常见陷阱。
如果你对自己的 Odoo 数据模型、自定义字段策略或技术实现有疑问,我们乐意和你一起评估并给出可执行的建议。 联系我们 让我们一起为你的项目找到合适的解决方案。