引言
在 Odoo 中,许多开发者默认会用 Many2one 来建立记录之间的关联——这在大多数场景下都很合适。但当某个关联可能指向不同类型的单据时(例如有时要关联销售订单,有时要关联采购订单或工单),Many2one 就显得不够灵活。为了解决这种“多模型”指向需求,Odoo 提供了专门的引用字段(Reference)。
引用字段是 Odoo ORM 里较为灵活的一类字段。与固定指向某一模型的 Many2one 不同,Reference 允许在预先定义的模型清单中选择任意一个模型,然后再选取具体记录:先选类型(如销售订单、发票、项目任务),再选记录。这样就能实现一种多态的关联,适应不同的业务流。
本文将介绍引用字段实际保存了什么数据、在 Odoo 数据模型中的行为、如何通过 Studio 或 Python 创建与定制,以及若干实用的业务实例,帮助你判断何时该用引用字段以及如何稳妥使用。
什么是 Odoo 的引用字段(Reference)
在 Odoo 的 ORM 里,引用字段是一种能指向多个模型中任意一个记录的特殊字段。你通过它的 selection 列表定义允许被引用的模型集合,这一机制是其与 Many2one 最大的不同点。
在数据库层面,引用字段把值以纯文本形式保存为“model_name,record_id”的字符串。例如对销售订单 42 的引用会存成 sale.order,42。如果需要直接写 SQL 查询或做低级筛选,这个存储格式必须牢记于心。
从界面交互来看,引用字段通常表现为两步选择:先在下拉中选择文档类型(如销售单、发票、任务),然后在对应模型的记录搜索窗里挑选具体条目。对用户来说,熟悉之后这种交互既直观又省去为每种单据建单独字段的麻烦。
下面给出在 Python 中定义引用字段的基本写法示例:
from odoo import fields, models
class HelpDeskTicket(models.Model):
_inherit = 'helpdesk.ticket'
related_document = fields.Reference(
selection=[
('sale.order', 'Sale Order'),
('purchase.order', 'Purchase Order'),
('account.move', 'Invoice'),
('project.task', 'Project Task'),
],
string='Related Document',
)
selection 参数是一个元组列表:每项包含技术模型名(如 sale.order)和面向用户的标签。通过控制这个列表,你决定用户在下拉里能看到并选择哪些模型。
也可以用动态方式生成 selection——在运行时从 ir.model 拉取模型列表,或由方法返回需要的集合。这个方式适合高度可配置的场景,但如果放开太多模型,会让最终用户无从选择。
在 Odoo Studio 里,引用字段以“Reference”类型出现。用 Studio 添加时,可以通过可视化界面直接勾选可选模型,无需写代码。对于快速定制和业务人员自助扩展表单非常方便。
字段的工作原理概述
理解引用字段的数据存储与读取方式,是在 Odoo 上正确使用它并避免陷阱的前提。
数据库中的存储细节
与只存整数外键的 Many2one 不同,引用字段在数据库里保存的是包含模型名和记录 ID 的字符串(如 sale.order,15),通常映射到 PostgreSQL 的 VARCHAR 列。因此数据库层面不会对它施加外键约束——这是为了支持多模型指向的设计取舍。
因为没有数据库级的外键约束,引用字段不会在被引用记录被删除时自动清理。若某个销售订单被删除,指向它的引用字段仍会保留原字符串。这一点在设计数据清理或报表时必须考虑到。
在 Python 中如何访问被引用记录
在代码中读取引用字段时,Odoo 会把它解析为对应模型的记录集对象(record)。你可以像操作 Many2one 那样访问其字段;若字段为空则返回 False。
ticket = self.env['helpdesk.ticket'].browse(1)
doc = ticket.related_document
if doc:
print(doc._name) # e.g. 'sale.order'
print(doc.name) # e.g. 'S00042'
print(doc.id) # e.g. 15
这体现了 Odoo ORM 的便利性:尽管底层是字符串存储,框架会在访问时把它解析回实际的记录对象供你直接使用。
关键的字段属性说明
下面是引用字段最常用、最需要关注的配置项:
- selection:定义可被选择的模型列表。也可以用方法名字符串,让方法在运行时返回列表。
- string:字段在界面上显示的标签。
- required:设置为必填时,用户必须同时选择模型类型与具体记录才能保存。
- readonly:将字段设为只读,常用于引用由程序自动写入、用户不可更改的场景。
- help:鼠标悬浮时显示的提示文本,用来指导用户如何选择合适的目标文档。
- compute:引用字段也可以作为计算字段,由 Python 方法动态生成值,适合根据业务规则自动关联目标记录。
筛选与搜索的特殊性
由于字段以字符串存储,写域(domain)或搜索条件时必须使用字符串格式。要查找关联到某条具体文档的记录,需要把 model,id 拼成完整字符串进行比较。
tickets = self.env['helpdesk.ticket'].search([
('related_document', '=', 'sale.order,15')
])
也可以用 like 操作符按模型类型筛选:
tickets = self.env['helpdesk.ticket'].search([
('related_document', 'like', 'sale.order,')
])
在设计依赖该字段的报表或计算字段时,记住它的字符串筛选行为会与 Many2one 的方式不同,必要时需要额外处理或写辅助字段。
适用的业务场景
引用字段最能体现价值的场景是一处关联需要根据情境指向不同类型文档的业务。以下列出五个常见且实用的实例。
1. 工单/客服单可关联任意单据
客服工单涉及的问题可能来源多样:发票争议、交付异常、合同问题或产品返修等。与其为每种单据各建一个字段,不如用一个引用字段让客服在单据类型下拉中先选类别(发票、销售单、交付单等),再选具体记录。这样所有上下文都集中在同一字段里,界面更简洁。
2. CRM 跟进活动关联多种来源单据
销售活动可能来源于潜在客户、报价、合同或支持工单等。给自定义的活动或 CRM 记录加上引用字段,销售人员就可以将活动标注为来源于任意单据类型,而不用被限定到单一模型,这在 CRM 定制中非常实用。
3. 跨模块的批注与备注系统
有的企业希望建立统一的内部备注模型以便横跨客户、项目、生产或采购等对象记录观测与评论。引用字段能让一条备注同时挂在客户、任务、制造单或采购单上,避免为每种对象复制备注模型。
4. 通用审批流指向任意单据
通用审批流程需要指明被审批的目标:采购单、报销单、请假单或合同等都有可能。审批请求模型上使用引用字段即可让同一套审批逻辑复用到多种单据,而不必为每种类型单独建审批模型。
5. 费用单可关联项目或销售订单
在咨询或专业服务行业,一笔费用可能要挂在项目或销售订单上,取决于费用性质。把 project.project 与 sale.order 同时放入引用字段的选择列表,能让财务灵活地把费用附着到合适的对象上。
如何创建或自定义引用字段
向模型添加引用字段有两种典型方式:通过 Odoo Studio 的无代码操作,或在 Python 中编码以获得更高的可控性。
使用 Odoo Studio
在 Studio 中添加引用字段非常直接:打开要扩展的表单,进入字段面板,选择 Reference 类型并可视化勾选允许的模型。Studio 会把该字段以 x_ 前缀的手工字段形式保存,适合业务分析师快速原型与配置。
这种方式适合快速交付与无需开发资源的改动。但要注意:Studio 创建的引用字段在实现动态 selection 或复杂计算逻辑时可能受限,复杂场景仍建议用代码实现。
在 Python 中实现引用字段的技术细节
如果做正规开发,建议在模型里直接定义引用字段。下面示例展示如何用方法动态生成 selection 列表:
from odoo import api, fields, models
class ApprovalRequest(models.Model):
_name = 'approval.request'
_description = 'Approval Request'
name = fields.Char(string='Request Name', required=True)
@api.model
def _get_document_types(self):
return [
('purchase.order', 'Purchase Order'),
('hr.expense.sheet', 'Expense Report'),
('hr.leave', 'Time Off Request'),
('sale.order', 'Sale Order'),
]
document_ref = fields.Reference(
selection='_get_document_types',
string='Document',
help='Select the document this approval relates to.',
)
把 selection 设置为方法名(字符串形式)可以在运行时加入条件逻辑:根据已安装模块、配置项或权限动态返回允许的模型列表,提高灵活性与可维护性。
通过 XML-RPC API 创建字段
也可以用 Odoo 的 XML-RPC API 远程创建引用字段,适合把字段作为远程配置或部署脚本的一部分。字段类型为 reference,selection 以字符串形式传入。
field_id = models.execute_kw(
ODOO_DB, uid, ODOO_API_KEY,
'ir.model.fields', 'create',
[{
'name': 'x_related_document',
'field_description': 'Related Document',
'model_id': model_id,
'ttype': 'reference',
'selection': "[('sale.order', 'Sale Order'), ('purchase.order', 'Purchase Order')]",
'state': 'manual',
}]
)
通过 API 创建时,selection 以可被 Python 解释的字符串形式传入,这是 ir.model.fields 表里存储该配置的方式。
最佳实践建议
使用引用字段时应遵循的要点汇总
- 保持选择列表简短且有针对性。不要因为能塞很多模型就全部放进去,应仅包含与业务场景真正相关的单据类型,否则会增加用户误选的风险。
- 如果始终指向同一模型,请使用 Many2one。引用字段的优势在于多模型指向;若目标模型固定,Many2one 更简单且便于查询与报表处理。
- 在计算字段中始终检查空值。当引用字段为空时在 Python 里返回 False,访问前应做判空以避免异常。
- 对“孤立引用”做定期清理。数据库不会自动清除指向已删除记录的字符串,建议通过自动化动作或定时任务定期扫描并清理失效引用。
- 在 selection 的标签使用可读性强的描述。下拉显示的是你在元组第二项写的文本,应使用面向业务的描述(如“客户发票”),而不是技术模型名。
- 在技术文档里清楚说明字段为何选用引用类型。因为它与 Many2one 在行为上有差异,后继开发者需要知道选择的原因和允许的模型集合。
常见错误与陷阱
常见错误及如何避免
把引用字段当成 Many2one 来写域过滤是个常见错误。许多开发者会尝试写 ('document_ref','=',15) 这样的域,但引用字段里保存的是 'sale.order,15' 这样的字符串,必须拼出完整字符串才能匹配。
忘记处理删除记录导致的孤立值。删除被引用的记录并不会自动清空引用字段;当引用失效时,读取字段会返回 False。任何依赖被引用记录存在性的代码都要做好容错处理。
滥用动态“全模型”选择功能。把 ir.model 里所有模型都放进 selection 看似灵活,但对最终用户来说会造成过多选项、选择错误和数据混乱。应限制为经过策划的可选模型集合。
指望内置的分组聚合按引用字段自然生效。由于引用字段是纯文本存储,Odoo 的默认 group-by 与透视逻辑不会像处理 Many2one 那样直接工作。若需要按引用目标分组,应额外建计算字段或拆出模型名作为单独字段来支持报表。
在 Studio 中混淆 Reference 与 Many2one。两者都能“关联记录”,但语义不同:Many2one 在创建时就绑定目标模型;Reference 在每次填写时让用户选模型。如果误选了 Many2one 而实际需要多模型指向,通常需要重新建字段。
总结要点
引用字段弥补了 Many2one 在“需要指向多种单据类型”的场景下的不足。当业务需要灵活将一处关联连到不同模型的文档时,引用字段是合适的工具。它支持 Studio 无代码配置,也能在 Python 中以更细粒度控制实现。
使用时务必记住三点:它以 'model,id' 字符串保存、数据库不会自动清理已删记录、在过滤和报表时需用拼接字符串或辅助字段来处理。理解这些差异后,引用字段在项目中会表现得稳定且可维护。
无论是构建通用审批流、把客服单灵活关联到不同单据,还是做跨模块的统一备注系统,引用字段都能让你的数据模型更简洁、逻辑更统一,而不必为每种文档类型重复实现相同功能。
我们在 Dasolo 提供 Odoo 实施、定制与优化服务,帮助企业把系统按真实业务流程落地。无论是自定义字段逻辑、从零设计数据模型,还是扩展现有实例,我们都有丰富的技术经验来完成高质量交付。
总结
如果你在 Odoo 项目中遇到关于字段类型选择、数据架构或开发实践的问题,欢迎联系咨询。我们可以基于你的业务场景给出可执行的设计与实施建议。
联系 Dasolo
无论你是在搭建通用的审批流、把工单关联到不同类型的文档,还是想做一个能跨模块记录备注的灵活系统,Reference 字段都能提供一种清爽且易维护的做法。它避免为每种模型类型重复实现相同逻辑,让你的代码更简洁、扩展更轻松。
需要我们协助实现您的 Odoo 项目吗?
在 Dasolo,我们专注于将 Odoo 与企业真实业务流程对接:从字段逻辑定制、从零设计数据模型,到在现有 Odoo 环境中增加新功能,我们具备深厚的技术能力,确保项目按正确方式交付并长期可维护。
如果你正在推进 Odoo 项目并在字段类型、数据架构或开发最佳实践上需要建议,欢迎联系咨询。我们可以一起评估现状,给出切实可行的方案与实施路径。