第8讲、Odoo 18 ORM 深度解析
Odoo 18 ORM 深度解析
Odoo 18 的 ORM(对象关系映射)是其最核心的部分之一,承担着模型定义、数据库操作、业务逻辑执行、权限控制、缓存机制等关键功能。Odoo ORM 通过 Python 类定义模型(Model),自动映射为 PostgreSQL 表,并借助丰富的装饰器、钩子和缓存机制实现功能增强。
🧠 一句话总结 Odoo ORM 原理
基于 Python 类、元编程和元模型注册机制,自动将类映射为 PostgreSQL 表,并提供统一的 CRUD、规则验证、上下文环境、事务控制与缓存优化的操作接口。
🧱 ORM 核心结构概览
odoo/
├── models/
│ ├── base.py # Model 基类,注册/字段定义/元类处理
│ ├── fields.py # 所有字段类型定义,如 Char、Many2one、One2many
│ ├── model.py # 核心模型类 Model 的实现
│ ├── registry.py # 注册表:管理所有模型类及数据库连接
│ ├── crud.py # 实际执行 CRUD 操作的底层实现
│ ├── osv/
│ │ ├── orm.py # 兼容老版本 osv API 的适配层
│ └── ...
🔄 ORM 生命周期与原理分析
1️⃣ 模型定义(Python class)
开发者通过如下方式定义模型:
class ResPartner(models.Model):
_name = "res.partner"
name = fields.Char()
active = fields.Boolean(default=True)
背后过程:
- 所有模型继承自
odoo.models.Model - 使用元类
MetaModel拦截类定义,抽取_name和字段定义,并注册到Registry - 字段通过
fields.*类定义(如Char,Boolean等),每个字段在内存中表示为Field实例,带有required,readonly,compute,inverse等属性
2️⃣ 模型注册(MetaModel & Registry)
# odoo.models.base.MetaModel
class MetaModel(type):
def __new__(mcs, name, bases, attrs):
# 收集字段、约束等元信息
...
# 注册模型到环境的注册表中
cls = super().__new__(...)
registry.register_model(cls)
# odoo.modules.registry.Registry
class Registry:
def __init__(self, dbname):
self.models = {} # 存储所有模型
...
3️⃣ 数据库表创建(自动同步)
安装模块时,Odoo 根据模型结构调用 tools.convert.convert_xml_import() 自动创建数据库表结构。
4️⃣ CRUD 操作流程
📝 以 create() 为例:
partner = self.env['res.partner'].create({'name': 'Tom'})
实际流程:
- 入口方法为
Model.create():def create(self, vals): self._check_create_rights() self._validate_fields(vals) ... ids = self._create(vals) return self.browse(ids) - 核心创建逻辑在
_create()方法中,通过cr.execute直接写入数据库 - 自动计算字段、约束校验、触发
@api.onchange,@api.model_create_multi,@api.constrains等钩子
🧩 CRUD 源码实现深度分析
Odoo ORM 的 CRUD(增删改查)操作不仅仅是 SQL 封装,还集成了权限校验、字段校验、钩子机制、批量优化、懒加载和缓存等多重机制。以 create() 为例,源码实现流程如下:
1. 统一入口:
所有模型都继承自 odoo.models.Model,开发者通过 self.env['model.name'].create(vals) 等方式调用 CRUD 方法。
2. create() 方法源码流程:
def create(self, vals):
self._check_create_rights()
self._validate_fields(vals)
...
ids = self._create(vals)
return self.browse(ids)
- 权限校验:
_check_create_rights()检查当前用户是否有创建权限 - 字段校验:
_validate_fields(vals)检查必填、只读、类型等约束 - 核心写入:
_create(vals)负责实际的数据库插入,底层通过cr.execute生成 SQL - 钩子机制:自动触发
@api.onchange,@api.model_create_multi,@api.constrains等装饰器方法 - 返回值:始终返回一个 recordset(即使只创建一条记录)
3. write()、unlink()、search() 实现:
- write(vals):批量更新,先校验权限和字段,再通过
cr.execute生成 UPDATE SQL,支持批量操作和自动触发相关钩子 - unlink():删除操作,先校验权限和规则,再通过
cr.execute生成 DELETE SQL,支持级联删除和钩子 - search(domain):查询操作,domain 转为 SQL WHERE,支持复杂条件、分页、排序,返回 recordset
4. 底层实现与优化:
- 底层 SQL 执行:所有 CRUD 最终都通过
self.env.cr.execute(sql, params)与 PostgreSQL 交互 - 批量优化:Odoo 会自动将多条操作合并为一条 SQL,减少 N+1 问题
- 懒加载:recordset 只在访问字段时才真正查询数据库
- 缓存机制:常用查询和字段值会被缓存,减少数据库压力
5. 权限与规则:
- 每次 CRUD 前都会自动调用
_check_access_rights(模型级)、_check_record_rules(记录级)、_check_company(多公司)等方法,保证数据安全
6. 钩子与扩展:
- 支持
@api.depends、@api.onchange、@api.constrains等装饰器,开发者可自定义业务逻辑 - 支持多种继承和 mixin,便于扩展和重载 CRUD 行为
7. 典型源码调用链(以 create 为例):
self.env['res.partner'].create(vals)- 进入
Model.create()(odoo/models/model.py) - 权限校验、字段校验
- 调用
_create(),底层cr.execute(INSERT ...) - 触发相关钩子
- 返回
self.browse(ids),即 recordset
5️⃣ 多记录操作的支持:recordset 机制
Odoo 的 ORM 是 "记录集优先" 的,即使只有一条记录,API 也始终返回一个 recordset。
# 多记录统一调用写操作
partners = self.env['res.partner'].browse([1, 2, 3])
partners.write({'active': False})
Recordset 是一个可迭代对象,封装了 ID 列表与当前环境 (env),具备懒加载、批处理优化等特性。
6️⃣ 计算字段机制(@api.depends)
total = fields.Float(compute='_compute_total')
@api.depends('amount', 'tax')
def _compute_total(self):
for rec in self:
rec.total = rec.amount + rec.tax
ORM 会自动根据依赖关系构建计算图,仅在相关字段变化时重新计算。
7️⃣ 缓存机制(ORM 缓存)
结合 odoo.tools.cache 模块的 @ormcache 实现自动缓存函数结果,如:
@ormcache('self.env.uid')
def get_partner_count(self):
...
字段值、recordset 本身也存在内存中做延迟加载(lazy load)。
8️⃣ 权限校验机制
CRUD 操作前调用权限方法:
_check_access_rights_check_record_rules_check_company(多公司支持)
支持从 UI、RPC、XML-RPC 层自动传入用户上下文校验。
🧰 ORM 特性总结表
| 特性 | 描述 | 相关源码模块 |
|---|---|---|
| 模型注册 | 所有模型通过元类注册 | base.py, registry.py |
| 字段定义 | 字段类封装类型与属性 | fields.py |
| 多记录集支持 | RecordSet 实现统一操作 | model.py |
| 动态计算 | 支持 @api.depends 自动刷新 | api.py |
| 数据校验 | _check_*, @constrains 机制 |
model.py |
| 缓存优化 | @ormcache, record cache |
tools/cache.py |
| 安全校验 | 权限 + 规则双重验证 | access.py, rules.py |
| 多语言支持 | 字段翻译机制 | translate.py |
| 上下文传递 | self.env 携带上下文 |
api.py |
| 多公司机制 | company_dependent, check_company |
fields.py, model.py |
🔄 ORM 实现图(简略)
┌───────────────┐
│ Python 模型类 │
└───────┬───────┘
│
▼
MetaModel
│
▼
注册表 Registry
│
▼
数据库表映射
│
▼
字段类型 (fields.X)
│
▼
CRUD 方法封装 (create/write/browse/search)
│
▼
权限 + 缓存 + 依赖 + API 修饰器
│
▼
PostgreSQL 数据库
✅ 总结一句话
Odoo ORM 是围绕"声明式模型 + 元编程注册 + 懒加载记录集 + 事务安全 + 上下文感知"的强大抽象层,它屏蔽了 SQL 细节,统一了数据与业务操作,极大提升了企业级开发的效率与安全性。
🧩 ORM 技术深度解读
1. 元编程与元类机制
Odoo ORM 的核心在于 Python 的元类(MetaModel),它不仅负责模型注册,还实现了字段收集、继承链处理、自动生成 SQL 映射等。
- 字段收集与继承:MetaModel 会递归父类,合并所有字段定义,支持多重继承和字段重载。
- 自动生成表结构:通过分析
_name、字段类型、约束等,自动生成 PostgreSQL 的表结构和索引。 - 模型生命周期钩子:如
__init__,__new__,__setup__,用于模型初始化和扩展。
2. 字段类型与高级特性
- 关系字段:如
Many2one,One2many,Many2many,自动处理外键、联表、级联删除等。 - 动态属性:如
compute,inverse,search,related,实现自动计算、反向写入、自定义搜索等。 - 字段扩展:支持
selection,context_dependent,company_dependent等高级特性。
3. 环境(env)与上下文机制
self.env是 Odoo ORM 的"上下文载体",包含当前用户、公司、语言、事务等信息。- 支持多数据库(多租户)、多公司、动态上下文切换,保证数据隔离与权限安全。
4. 事务与一致性保障
- 所有操作都在 PostgreSQL 事务中执行,支持自动回滚、嵌套事务(savepoint)、乐观锁等。
- 支持
@api.autovacuum,@api.model_cr等钩子,自动维护数据一致性和表结构升级。
5. 性能优化机制
- 延迟加载(Lazy Load):Recordset 只在访问字段时才查询数据库,避免不必要的 SQL。
- 批量操作优化:如
write,unlink支持批量 SQL,减少循环和 N+1 查询。 - 缓存机制:
@ormcache支持参数化缓存,字段值缓存,减少重复计算和数据库访问。
6. 安全与权限体系
- 多层权限校验:模型级(access rights)、记录级(record rules)、字段级(field access)、公司级(multi-company)。
- 上下文感知:权限校验自动感知当前用户、公司、上下文,支持自定义规则和动态调整。
7. 扩展性与自定义能力
- 支持通过继承(_inherit)、mixin、装饰器(@api.model, @api.depends, @api.constrains)等方式灵活扩展模型和业务逻辑。
- 支持模块化开发,模型和字段可被多个模块动态扩展和重载。
8. 与外部系统集成
- ORM 屏蔽了 SQL 细节,支持通过 RPC、XML-RPC、JSON-RPC、REST API 等多种方式与外部系统交互。
- 支持自动序列化、反序列化、权限校验和上下文传递。
9. 典型源码流程图
10. 常见开发/调试技巧
- 使用
self.env.cr直接执行 SQL 以优化特殊场景。 - 利用
@api.model_create_multi优化批量创建性能。 - 善用
@api.depends精确声明依赖,避免不必要的计算。 - 通过
sudo()、with_context()、with_company()灵活切换权限和上下文。

浙公网安备 33010602011771号