Presentación rápida
En Odoo, la mayoría de desarrolladores recurre al campo Many2one para enlazar registros entre modelos porque es sencillo y eficiente. Sin embargo, hay situaciones en las que el enlace debe poder apuntar a distintos tipos de documentos según el contexto —por ejemplo: un pedido de venta, una orden de compra o una tarea de proyecto— y ahí es donde entra el campo Reference: una solución pensada para relaciones polimórficas.
El campo Reference es una opción flexible dentro del ORM de Odoo que permite apuntar a registros de diferentes modelos definidos en su lista de selección. En la práctica el usuario elige primero el tipo de documento y luego el registro específico, de modo que una única propiedad puede representar enlaces a varios modelos sin crear múltiples campos redundantes.
Esta guía explica qué guarda realmente un campo Reference, cómo se comporta en la base de datos y en Python, las formas de añadirlo (desde Odoo Studio o mediante código) y ejemplos prácticos en los que aporta valor real a procesos de negocio.
¿Qué es el campo Reference en Odoo?
En el modelo de datos de Odoo, Reference es un tipo especial pensado para enlaces polimórficos: en lugar de apuntar siempre a un modelo fijo (como hace Many2one), acepta una lista de modelos válidos y almacena cuál de ellos y qué registro concreto está referenciado.
A nivel de base de datos la información se guarda como una cadena de texto con el formato modelo,id —por ejemplo: sale.order,42— en una columna VARCHAR. Esto es importante cuando necesitas hacer consultas SQL directas o entender por qué no existen claves foráneas que garanticen integridad referencial.
En la interfaz el campo se comporta como un selector en dos pasos: primero se escoge el tipo de documento (Factura, Pedido de venta, Tarea...), y después se busca y selecciona el registro dentro de ese modelo. Es intuitivo una vez que los usuarios comprenden el flujo de selección en dos fases.
Ejemplo sintético de definición en 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',
)
El argumento selection consta de tuplas (nombre_técnico_del_modelo, etiqueta_visible). Ahí decides qué modelos aparecen en el desplegable y qué texto verá el usuario para cada uno.
También existe la posibilidad de generar esa lista de forma dinámica leyendo modelos desde ir.model, lo que puede ser útil en soluciones muy configurables, aunque conviene filtrar para no abrumar al usuario con demasiadas opciones.
En Odoo Studio encuentras el tipo de campo como Reference en el panel de campos. Añadirlo desde Studio permite seleccionar los modelos directamente desde la interfaz, sin programar, lo que lo hace accesible para analistas funcionales y administradores que quieren hacer personalizaciones rápidas.
Cómo funciona internamente
Comprender cómo se almacena y recupera la información desde un campo Reference es clave para diseñar soluciones robustas en Odoo y evitar sorpresas al consultar, filtrar o mantener datos.
Almacenamiento en la base de datos
A diferencia de Many2one, que guarda únicamente un entero (la clave foránea), Reference guarda una cadena con el par modelo,id, por ejemplo sale.order,15, en una columna VARCHAR. Esa decisión de diseño permite la polimorfía pero impide que PostgreSQL aplique restricciones de integridad referencial a nivel de columna.
Por esa misma razón, Odoo no limpia automáticamente los valores cuando el registro referenciado se elimina. Si borras un pedido, cualquier Reference que apuntara a él quedará con la antigua cadena. Es un comportamiento intencional que hay que tener en cuenta al diseñar mantenimientos y comprobaciones de datos.
Accediendo al registro referenciado desde Python
Cuando lees un Reference en código, Odoo resuelve y devuelve el objeto record del modelo apuntado, de forma similar a un Many2one. Si el campo está vacío devuelve False, así que conviene verificar su existencia antes de usarlo.
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
Aunque internamente sea una cadena, el ORM abstrae esa representación y te permite trabajar con el registro como un objeto normal cuando lo recuperas en Python.
Atributos clave del campo
Estos son los parámetros más útiles que puedes configurar en un campo Reference dentro del framework de Odoo:
- selection: lista de tuplas o nombre de método que devuelve la lista de modelos permitidos. Permite hacerlo estático o dinámico.
- string: etiqueta que verá el usuario en los formularios.
- required: si se marca, obliga a elegir tipo y registro antes de guardar.
- readonly: evita que el usuario cambie el valor desde la interfaz; útil cuando la referencia la fija código.
- help: texto de ayuda que aparece al pasar el ratón sobre la etiqueta, ideal para orientar al usuario.
- compute: como cualquier campo, puede ser computado por método Python para asignaciones automáticas según la lógica de negocio.
Filtrado y búsqueda
Debido a que el valor es una cadena, los dominios y búsquedas deben construirse con esa sintaxis completa. No puedes filtrar por id como si fuera un Many2one:
tickets = self.env['helpdesk.ticket'].search([
('related_document', '=', 'sale.order,15')
])
También puedes filtrar por tipo de modelo usando like:
tickets = self.env['helpdesk.ticket'].search([
('related_document', 'like', 'sale.order,')
])
Ten presente este comportamiento cuando redactes informes o campos computados que dependan de Reference: su filtrado y agrupado difieren de los Many2one tradicionales.
Casos de uso en la empresa
Cuando usar Reference: situaciones prácticas
1) Tickets de soporte que necesitan enlazarse a cualquier documento
Un equipo de soporte puede gestionar incidencias relacionadas con facturas, albaranes, contratos o pedidos. En vez de crear un campo para cada tipo, un único Reference en el ticket permite adjuntar el documento aplicable seleccionando primero el tipo y luego el registro concreto.
2) Actividades comerciales que parten de orígenes diversos
En ventas, una tarea de seguimiento puede provenir de un lead, un presupuesto, un contrato vigente o un caso de soporte. Un campo Reference en la actividad permite al comercial relacionar la acción con su origen sin limitarse a un único modelo.
3) Notas y anotaciones transversales
Si tu empresa guarda notas internas que deben poder adjuntarse a clientes, proyectos, órdenes de fabricación o compras, un modelo de nota con un campo Reference evita crear múltiples tablas de notas y centraliza toda la información.
4) Flujo genérico de aprobaciones
Un sistema de aprobaciones que debe cubrir pedidos de compra, gastos, ausencias y contratos se beneficia de un campo Reference en la solicitud de aprobación: un solo modelo de aprobación puede apuntar a distintos documentos sin replicar lógica.
5) Gastos vinculados a proyecto u orden de venta
En consultoría o servicios profesionales, un gasto puede pertenecer a un proyecto o a un pedido de cliente según el caso. Un Reference que permita project.project y sale.order facilita que el contable vincule el gasto a la referencia correcta.
Cómo crear o personalizar un campo Reference
Formas de añadir un campo Reference
Hay dos vías principales: Odoo Studio para un enfoque sin código, o código Python para control total y flexibilidad avanzada.
Con Odoo Studio
Studio permite insertar un campo Reference desde el editor visual y elegir los modelos que aparecerán en el desplegable sin escribir una sola línea de código. El campo creado recibe el prefijo x_ y es la opción más rápida para personalizaciones de negocio; sin embargo, tiene limitaciones para configuraciones más complejas como selección dinámica o comportamiento computado avanzado.
Implementación técnica en Python
Para desarrollo completo, define el campo en el modelo Python. Esto te permite usar métodos dinámicos, validaciones, y lógica adicional. A continuación, un ejemplo que muestra una selección construida por un método:
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.',
)
Pasar el nombre del método como string en selection te permite construir la lista con lógica condicional —filtrar por módulos instalados, por permisos, o por configuración— y así ofrecer sólo las opciones relevantes.
Creación mediante la API XML-RPC
También puedes crear el campo vía XML-RPC cuando despliegas configuraciones de forma remota. El tipo a utilizar en ir.model.fields es reference y la selección se envía como cadena representando la lista Python:
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',
}]
)
Recuerda que la API espera la selección como una cadena evaluable por Python; así es como Odoo la almacena en ir.model.fields cuando se crea desde código o API.
Buenas prácticas
Pautas y recomendaciones
- Limita la lista de selección a lo realmente útil. No incluyas todos los modelos posibles solo porque puedes; cuantos menos y más relevantes sean los tipos, más fácil será la experiencia de usuario.
- Si siempre apuntas a un único modelo, usa Many2one. Many2one es más eficiente para consultas, reportes y operaciones habituales; Reference debe reservarse para relaciones que cambian de tipo según el caso.
- Comprueba valores nulos en campos computados. Un Reference vacío devuelve False en Python; cualquier código que acceda al registro referenciado debe validar la existencia para evitar excepciones.
- Gestiona referencias huérfanas con acciones automatizadas. Dado que no hay clave foránea, crea tareas programadas o reglas que detecten y limpien referencias a registros eliminados.
- Usa etiquetas descriptivas para el usuario. La segunda parte de cada tupla en selection es lo que ve el usuario: escribe nombres claros y orientados al negocio, por ejemplo «Factura de cliente» en lugar de dejar la referencia técnica del modelo.
- Documenta por qué y cómo se usa. En las especificaciones técnicas explica la razón de elegir Reference y qué modelos incluye; así los desarrolladores posteriores entenderán el diseño y tomarán decisiones coherentes.
Errores habituales
Errores y malas prácticas comunes
Tratarlo como un Many2one en dominios
Es frecuente intentar filtrar usando solo el ID, por ejemplo ('document_ref','=',15). Eso falla porque el valor almacenado es 'modelo,15'. Siempre compón la cadena completa cuando construyas dominios sobre un Reference.
Olvidar que los registros borrados dejan valores huérfanos
Al borrar el registro apuntado, el Reference conserva la cadena antigua. Al leerlo Odoo devolverá False en lugar de lanzar un error, por lo que tu lógica debe contemplar este escenario y limpiar o notificar según convenga.
Abusar de una selección dinámica que incluya todos los modelos
Pedir todos los modelos desde ir.model puede producir un desplegable inmanejable. Evita mostrar cientos de opciones en un campo de cara al usuario; mejor restringe la lista a los tipos con sentido para el proceso.
Esperar agrupados nativos en informes
Como Reference es texto, las funcionalidades de groupby/pivote no funcionan igual que con Many2one. Si necesitas agrupar por tipo de documento, crea un campo computado que extraiga el nombre del modelo o una selección adicional para facilitar los informes.
Confundir Reference y Many2one en Studio
En Studio algunos usuarios eligen el campo equivocado porque ambos sirven para enlazar registros. Recuerda: Many2one está atado a un solo modelo desde su creación; Reference permite elegir el modelo en cada uso. Si creaste un Many2one donde hacía falta un Reference, tendrás que recrear el campo correctamente.
Resumen final
Conclusión: cuándo y por qué usar Reference
Reference resuelve el problema de enlaces que deben apuntar a distintos tipos de documentos según el contexto. Ofrece una implementación sencilla, es compatible con Studio para soluciones sin código y se integra bien en modelos Python cuando se necesita mayor control. Las diferencias clave —almacenamiento como cadena, ausencia de limpieza automática y forma de filtrar— deben guiar tu diseño para evitar problemas posteriores.
Si tu necesidad es enlazar tickets, aprobaciones, notas o gastos a varios modelos sin multiplicar estructura y lógica, Reference te da una solución limpia y mantenible que evita duplicar modelos y comportamientos por cada tipo de documento.
¿Necesitas ayuda con tu implementación de Odoo?
En Dasolo acompañamos a empresas en implementaciones, personalizaciones y optimización de Odoo para que el sistema refleje procesos reales. Desde el diseño de modelo de datos hasta la implementación de lógica a medida, ayudamos a que la plataforma funcione de forma fiable y escalable.
Si estás desplegando un proyecto Odoo y necesitas orientación sobre tipos de campo, arquitectura de datos o buenas prácticas de desarrollo, podemos ayudarte a evaluar la solución adecuada y a ejecutarla con calidad técnica.