Relaciones entre modelos en Odoo: Many2one, One2many y Many2many sin liarte
Las tres relaciones que conectan tus datos, qué guardan de verdad en la base de datos y los comandos para escribirlas sin romper nada.
Forma parte de nuestra guía completa: Desarrollo de módulos en Odoo.
Un modelo aislado en Odoo es poca cosa: una tabla con sus campos y poco más. La potencia aparece cuando los conectas. Una propiedad pertenece a un vendedor, tiene varias ofertas y se etiqueta con varias categorías. Esas tres frases son, exactamente, las tres relaciones de Odoo: Many2one, One2many y Many2many. Suenan a jerga, pero en cuanto entiendes qué guarda cada una en la base de datos, dejan de dar miedo y empiezas a modelar como es debido.
El malentendido que arrastra casi todo el mundo es creer que las tres "guardan datos" igual. No. Y esa diferencia explica el 90% de los errores de rendimiento y de los "¿por qué no se borra esto?".
Many2one: la clave foránea de toda la vida
Es la relación reina, la que más usarás. "Esta propiedad tiene un vendedor." Por debajo es una columna en tu tabla con el id del registro relacionado. Nada exótico: una foreign key.
class Property(models.Model):
_name = "estate.property"
salesperson_id = fields.Many2one(
"res.users", string="Vendedor",
default=lambda self: self.env.user,
)
buyer_id = fields.Many2one("res.partner", string="Comprador")
Lo que mucha gente pasa por alto es ondelete. Por defecto, si borras el vendedor, el campo se pone a vacío (set null). Pero a veces quieres restrict (no me dejes borrar un vendedor que tiene propiedades) o cascade (si se va el padre, que se vayan los hijos). Decidir esto a conciencia evita registros huérfanos y sustos en producción.
One2many: un espejo, no una columna
Aquí está la trampa conceptual más importante de todo Odoo. Un One2many no guarda nada en tu tabla. Es la cara opuesta de un Many2one que vive en el otro modelo. Es, literalmente, una vista calculada: "dame todos los registros de allá que me apuntan a mí".
# En estate.property
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Ofertas")
# En estate.property.offer (¡aquí está la columna de verdad!)
property_id = fields.Many2one("estate.property", required=True, ondelete="cascade")
Por eso un One2many siempre necesita su Many2one inverso: sin esa columna en el otro lado, no hay nada que mirar. El error clásico del principiante es definir el One2many y esperar que funcione solo. No funciona, y el mensaje de error no siempre es obvio.
Many2many: la tabla intermedia que Odoo gestiona por ti
"Una propiedad tiene varias etiquetas, y una etiqueta está en varias propiedades." Eso es un Many2many, y por debajo Odoo crea una tabla intermedia (la relation table) sin que tú la veas.
tag_ids = fields.Many2many("estate.property.tag", string="Etiquetas")
Cómodo, pero con letra pequeña: las consultas sobre Many2many con muchos registros pueden volverse pesadas, y a veces lo que de verdad necesitas es un modelo intermedio explícito (un One2many a una tabla puente) porque quieres guardar datos sobre la relación —una fecha, un estado, un importe—. Cuando veas que tu Many2many "querría tener campos", esa es la señal de que necesitas un modelo intermedio.
Cómo se escriben las relaciones (esto vale oro)
Aquí es donde la documentación pierde a mucha gente. No escribes en un One2many o Many2many como en un campo normal; usas comandos. En Odoo moderno hay helpers legibles:
from odoo.fields import Command
property.write({
"offer_ids": [
Command.create({"price": 250000, "partner_id": partner.id}), # crear y enlazar
Command.unlink(old_offer.id), # borrar el registro
],
"tag_ids": [
Command.set([tag_a.id, tag_b.id]), # reemplazar el conjunto entero
Command.link(tag_c.id), # enlazar uno existente
],
})
Los verás también como tuplas crípticas —(0, 0, {...}), (4, id), (6, 0, [ids])— en código antiguo; son lo mismo. El día que entiendes que Command.set reemplaza y Command.link añade, dejas de duplicar etiquetas sin querer y de borrar relaciones que creías conservar.
Atajos que se agradecen: campos related
Una vez tienes relaciones, puedes "traer" campos de un modelo a otro sin recalcular nada, con un campo related. Práctico para mostrar el dato del padre en el hijo:
buyer_phone = fields.Char(related="buyer_id.phone", string="Teléfono del comprador")
Es cómodo, pero recuerda lo que vimos sobre campos calculados y store: si vas a filtrar o agrupar por ese related, tendrás que valorar guardarlo. Lo barato sale caro cuando el listado tiene 50.000 filas.
Lo que separa un modelo de datos sano de uno enfermo
En auditorías, el patrón se repite: relaciones que deberían ser Many2one resueltas con un Char ("escribe aquí el nombre del cliente") que destroza la integridad; Many2many donde hacía falta un modelo intermedio; One2many sin ondelete que dejan basura por todas partes. Ninguno de esos errores se ve en la demo. Todos se ven a los dos años, cuando los datos ya están sucios y migrar duele.
Modelar bien las relaciones es, sin exagerar, la decisión técnica que más condiciona la vida de tu Odoo. Si arrancas un desarrollo a medida, esto se diseña antes de escribir la primera vista. Lo abordamos en la anatomía de un módulo y, cuando toca evolucionarlo sin romperlo, en la herencia en Odoo.
¿Tu modelo de datos ya te está dando guerra, o vas a empezar uno nuevo y quieres hacerlo bien a la primera? Cuéntanoslo en una sesión de diagnóstico o mira cómo enfocamos el desarrollo a medida.
Comentarios (0)
Sé el primero en comentar.
Inicia sesión para dejar un comentario.
AccederLos comentarios se revisan antes de publicarse.
Artículos relacionados
Acciones automatizadas en Odoo: deja que el ERP haga el trabajo aburrido
Acciones de servidor, reglas de automatización y tareas planificadas: las tres piezas para que Odoo reaccione, avise y ejecute solo, sin que nadie se acuerde.
Informes PDF con QWeb en Odoo: del formulario al documento que cierra ventas
QWeb es HTML que se convierte en PDF. Te enseñamos a montar un informe, recorrer registros con t-foreach y por qué t-field te ahorra mil dolores de cabeza.
Herencia en Odoo: la función que decide si tu código sobrevive a las actualizaciones
Extensión clásica, delegación y herencia de vistas. Las tres formas de modificar Odoo sin tocar el core —y por qué heredar en vez de copiar te ahorra cada migración.