第十二章 继承机制
篇别:第一篇 基础知识
本章学习目标
- 准确区分 经典继承(_inherit)、代理继承(_inherits)、原型继承(_inherit + _name) 的表结构与使用场景。
- 能在实际项目中选择 委托继承 或 Many2one 扩展 并说明权衡。
- 掌握 视图继承 与 Mixin 列表 的写法,并理解 MRO 对方法解析的影响。
- 能阅读
_inherit链 与 技术菜单中的模型继承列表 做升级影响分析。
导读:继承是 Odoo 扩展的核心语法
Odoo 很少通过「改核心源码」交付定制,而是 在独立模块中继承:扩展模型字段/方法、继承视图打补丁、组合 Mixin 获得消息/活动等能力。选错继承类型会导致 表结构冗余、联表性能差或升级困难。
12.1 经典继承(_inherit)
12.1.1 知识要点
- 形式:
class X(models.Model): _inherit = 'existing.model'(不写新_name或_name与父一致,依惯例为 扩展同名模型)。 - 效果:在 同一张业务表 上增加字段与方法(多模块可链式
_inherit,合并为同一模型类)。 - 适用:给
res.partner、sale.order等标准模型 加字段、改方法、加约束。
12.1.2 案例
案例 A:扩展伙伴
from odoo import models, fields
class Partner(models.Model):
_inherit = 'res.partner'
library_card = fields.Char('借书证号')
is_library_member = fields.Boolean('图书馆会员')
案例 B:重写方法(须 super)
class SaleOrder(models.Model):
_inherit = 'sale.order'
def action_confirm(self):
# 前置逻辑
res = super().action_confirm()
# 后置逻辑
return res
案例 C:多模块扩展同一模型
模块 A、B 均 _inherit = 'res.partner' → 最终 res.partner 合并字段与方法;方法顺序 由依赖与 MRO 决定。
12.1.3 截图占位

12.1.4 本节练习
- 实操:扩展
sale.order,增加library_note(Text),并在销售订单表单 视图继承 中显示(可与 8.14 联做)。 - 简答:经典继承新增字段在数据库中落在哪张表?
- 判断:两个模块定义同名方法且都未
super,最终行为一定可预测。( )
参考答案提示:2. res_partner(该模型对应表)。3. 错,依赖 MRO 与模块顺序,需规范 super。
12.2 代理继承(_inherits)
12.2.1 知识要点
- 形式:
_inherits = {'parent.model': 'delegate_field'},子模型有 Many2one 委托字段 指向父记录。 - 效果:访问子记录时 自动委托 读取/写入父模型字段(具体规则以 ORM 为准);子表 + 父表 存储。
- 典型:
product.product与product.template;HR 版本 中部分 employee/user 关系也曾用类似模式。 - 与 Many2one:
_inherits由框架维护 委托语义(创建/复制/cascade 行为),手写 Many2one 需自己处理同步与 ondelete。
12.2.2 案例(简化示意)
class LibraryMember(models.Model):
_name = 'library.member'
_description = '图书馆会员'
_inherits = {'res.partner': 'partner_id'}
partner_id = fields.Many2one(
'res.partner', required=True, ondelete='cascade',
)
level = fields.Selection([('normal', '普通'), ('vip', 'VIP')])
新建 library.member 时通常会 同时创建 关联 res.partner(框架行为需对照文档与测试验证)。
12.2.3 截图占位

12.2.4 本节练习
- 简答:
_inherits与「仅 Many2one + related 字段」在 创建流程 上的差异? - 实操:画出
library.member与res.partner的 ER 简图(两表 + 外键)。 - 情景:会员只想用
partner表即可,是否需要单独library.member?决策依据?
参考答案提示:1. _inherits 由 ORM 统一创建/委托;手写需自己封装 create/write。
12.3 原型继承(_inherit + _name)
12.3.1 知识要点
- 形式:新
_name,且_inherit = 'base.model'。 - 效果:复制/扩展 基模型结构得到 新模型,通常有 独立数据库表;适合 业务分叉(如
mail.message→ 特定子类,具体以官方模型为准)。 - 注意:与「经典继承」区别在 是否有新
_name与新表。
12.3.2 案例
class Animal(models.Model):
_name = 'zoo.animal'
_description = '动物'
name = fields.Char(required=True)
class Mammal(models.Model):
_name = 'zoo.mammal'
_inherit = 'zoo.animal'
gestation_days = fields.Integer('妊娠期(天)')
12.3.3 截图占位

12.3.4 本节练习
- 判断:原型继承后两张表完全独立。( )
- 简答:何时用原型继承而不用「独立模型 + Many2one」?
- 实操:查
_table或数据库\d确认zoo_mammal是否含name列(以实际 ORM 为准)。
参考答案提示:1. 通常有新表且字段合并策略依版本;需实测。2. 需共享大量基类行为/视图时代码复用更高。
12.4 视图继承(新增)
12.4.1 知识要点
与 第八章 8.14 一致:inherit_id、xpath、priority。
模型继承 与 视图继承 常同时使用:Python 加字段,XML 把字段摆到界面。
12.4.2 案例
<record id="view_order_form_inherit_library" model="ir.ui.view">
<field name="name">sale.order.form.inherit.library</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="priority">15</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='partner_id']" position="after">
<field name="library_note"/>
</xpath>
</field>
</record>
12.4.3 截图占位

12.4.4 本节练习
- 实操:两个模块继承同一表单:A
priority=10,Bpriority=20,观察字段最终顺序。 - 简答:视图继承与 重写整视图(复制 arch)相比的升级优劣?
参考答案提示:2. 继承更抗上游变更;整视图易碎但自由度大。
12.5 Mixin 继承模式(新增)
12.5.1 知识要点
- 形式:
_inherit = ['mail.thread', 'mail.activity.mixin', 'my.abstract.mixin']。 - Mixin:多为
AbstractModel(_abstract = True),不单独建业务表,被「混入」目标模型。 - MRO:多 mixin 时方法解析 从左到右;冲突应用
super()链 显式协作,避免覆盖丢失。
12.5.2 案例
class Loan(models.Model):
_name = 'library.loan'
_inherit = ['mail.thread', 'mail.activity.mixin']
name = fields.Char()
book_id = fields.Many2one('library.book')
获得:chatter、关注者、活动、消息子类型等。
12.5.3 截图占位

12.5.4 本节练习
- 简答:Mixin 方法冲突时,推荐的重构手段?
- 实操:临时注释一个 mixin,观察 数据库列 与 视图报错(理解依赖)。
- 判断:
mail.thread可以不继承任何模型单独安装使用。( )
参考答案提示:1. 抽第三种 mixin 或调整 _inherit 顺序并统一 super。3. 错,需混入具体模型。
本章综合练习
- 设计:
library.member委托res.partner,会员等级level在 partner 表单通过 视图继承 展示;写出 Python + XML 提纲。 - 对比表:经典 / 委托 / 原型 三种继承在 表数量、查询 JOIN、适用场景、升级风险 四列填表。
- 阅读:打开
product.product源码片段,标出_inherits与_inherit(若有)并写一句说明。 - 拓展:官方文档「Inheritance」中关于 Delegate 的警告(若有)摘录一条。
本章对应白皮书目录:第十二章 继承机制。