Odoo ORM 专家
概述
本技能深入讲解 Odoo 的对象关系映射器(ORM)。涵盖记录的读写操作、构建 domain 过滤器、处理关联字段,以及避免 N+1 查询等常见性能陷阱。
使用场景
- 编写
search()、browse()、create()、write()或unlink()调用 - 为视图或服务器动作构建复杂的 domain 过滤器
- 实现计算字段、存储字段和关联字段
- 调试慢查询或优化批量操作
工作流程
- 激活:提及
@odoo-orm-expert并描述你需要的数据操作 - 获取代码:接收正确、地道的 Odoo ORM 代码及解释
- 优化:请求对现有 ORM 代码进行性能审查
示例
示例 1:使用 Domain 过滤器搜索
# Find all confirmed sale orders for a specific customer, created this year
import datetime
start_of_year = datetime.date.today().replace(month=1, day=1).strftime('%Y-%m-%d')
orders = self.env['sale.order'].search([
('partner_id', '=', partner_id),
('state', '=', 'sale'),
('date_order', '>=', start_of_year),
], order='date_order desc', limit=50)
# Note: pass dates as 'YYYY-MM-DD' strings in domains,
# NOT as fields.Date objects — the ORM serializes them correctly.
示例 2:计算字段
total_order_count = fields.Integer(
string='Total Orders',
compute='_compute_total_order_count',
store=True
)
@api.depends('sale_order_ids')
def _compute_total_order_count(self):
for record in self:
record.total_order_count = len(record.sale_order_ids)
示例 3:安全的批量写入(避免 N+1)
# ✅ GOOD: One query for all records
partners = self.env['res.partner'].search([('country_id', '=', False)])
partners.write({'country_id': self.env.ref('base.us').id})
# ❌ BAD: Triggers a separate query per record
for partner in partners:
partner.country_id = self.env.ref('base.us').id
最佳实践
- ✅ 推荐: 在记录集上使用
mapped()、filtered()和sorted(),而非 Python 循环 - ✅ 推荐: 谨慎使用
sudo(),仅在理解安全影响时使用 - ✅ 推荐: 只需要计数时,优先使用
search_count()而非len(search(...)) - ✅ 推荐: 使用
with_context(...)传递上下文值,而非直接修改self.env.context - ❌ 禁止: 在循环内调用
search()—— 这是 Odoo 头号性能杀手 - ❌ 禁止: 除非绝对必要,否则不要使用原生 SQL;所有标准操作都用 ORM
- ❌ 禁止: 将 Python
datetime/date对象直接传入 domain 元组 —— 始终转为'YYYY-MM-DD'字符串
限制
- 不深入涵盖
cr.execute()原生 SQL 模式 —— SQL 级优化请使用 Odoo 性能调优技能 - 存储计算字段在大规模场景下会产生显著的写入开销;本技能不涵盖分区策略
- 不涵盖瞬态模型(
models.TransientModel)或向导模式 - 由于配置覆盖,ORM 行为在 Odoo SaaS 和本地部署版本之间可能存在细微差异