Ir al contenido

Herencia en Odoo: _inherit vs _inherits explicado con ejemplos

Extensión clásica vs delegación — cuándo usar cada patrón y errores a evitar
16 de junio de 2026 por
Herencia en Odoo: _inherit vs _inherits explicado con ejemplos
Aitor Atencia

Odoo · Arquitectura · Herencia

Odoo ofrece dos mecanismos de herencia que suenan parecidos pero funcionan de forma muy distinta: _inherit extiende un modelo existente; _inherits delega en otro. Elegir mal es una de las fuentes más habituales de bugs y deuda técnica.

Logo Odoo Diagrama de herencia UML
Extender ≠ delegar. En Odoo son dos patrones ORM distintos.

Conceptos

Dos palabras, dos mecanismos

_inherit_inherits
Qué haceExtiende un modelo existente (clásico)Delega campos y comportamiento en otro modelo (composición)
Tabla en BDUna sola tabla (la del padre)Dos tablas enlazadas por Many2one
_nameOpcional (mismo nombre que el padre)Obligatorio (nombre nuevo del modelo)
Ejemplo coreMódulos que añaden campos a sale.orderproduct.productproduct.template
Cuándo usarloAñadir campos/métodos a un modelo estándarCrear entidad nueva que «es» otra cosa a la vez

Regla mnemotécnica: _inherit = «quiero más del mismo»; _inherits = «quiero un modelo nuevo que reutilice otro por dentro».

_inherit

Extensión clásica con _inherit

Es el patrón más usado en módulos custom. No creas un modelo nuevo: modificas uno existente. Todos los módulos que heredan el mismo modelo encadenan sus cambios gracias a super().

# contact_extension/models/res_partner.py
from odoo import api, fields, models

class ResPartner(models.Model):
    _inherit = 'res.partner'

    birth_date = fields.Date(string='Birth Date')
    age = fields.Integer(compute='_compute_age', store=True)

    @api.depends('birth_date')
    def _compute_age(self):
        today = fields.Date.today()
        for partner in self:
            partner.age = (
                today.year - partner.birth_date.year
                if partner.birth_date else 0
            )

El registro sigue siendo un res.partner. La tabla es res_partner. Las vistas del contacto muestran los nuevos campos si añades un XML con xpath.

Variantes de _inherit

  • Solo _inherit: extiendes el modelo sin cambiar su _name
  • _inherit + _name distinto: reemplazas/renombras (poco habitual)
  • Lista de mixins: _inherit = ['mail.thread', 'mail.activity.mixin']

_inherits

Delegación con _inherits

Creas un modelo nuevo cuyos campos del padre delegado aparecen como si fueran propios. En base de datos hay dos registros vinculados: uno en tu tabla y otro en la del padre.

Odoo backend
El patrón _inherits es el que usa Odoo en productos, usuarios y otros modelos compuestos.
# Ejemplo simplificado al estilo product.product / product.template
class ProductVariant(models.Model):
    _name = 'product.variant.custom'
    _description = 'Product Variant'
    _inherits = {{'product.template': 'template_id'}}

    template_id = fields.Many2one(
        'product.template', string='Template',
        required=True, ondelete='cascade',
    )
    sku_suffix = fields.Char(string='SKU Suffix')

    # Accedes a template_id.name como si fuera self.name
    # El ORM delega lectura/escritura al registro padre

Al crear un product.variant.custom, Odoo crea también el product.template asociado. Al borrar el hijo con ondelete='cascade', se elimina el padre.

Decisión

¿Cuándo usar cada uno?

Usa _inherit si…

  • Añades campos a un modelo estándar
  • Sobrescribes métodos (action_confirm, etc.)
  • Añades mixins (chatter, actividades)
  • No necesitas identidad de modelo separada

Usa _inherits si…

  • Tu entidad es conceptualmente «un X con extras»
  • Necesitas tabla y permisos propios
  • Quieres reutilizar campos del padre sin duplicarlos
  • El ciclo de vida del padre está acoplado al hijo

Ejemplo de dominio: un diagnostic.test.subject (perfil del menor) no debería ser un res.partner vía _inherits si buscas privacidad RGPD — mejor modelo separado con relación Many2one. La herencia por delegación une demasiado el ciclo de vida.

Errores

Errores habituales

ErrorConsecuenciaSolución
Usar _inherits para «añadir un campo»Dos tablas, joins extra, permisos duplicadosUsar _inherit
Olvidar super() en métodos sobrescritosRompe otros módulos en la cadenaSiempre llamar a super()
ondelete incorrecto en el Many2one padreHuérfanos o borrados en cascada inesperadoscascade o restrict según negocio
Confundir _inherit con herencia PythonExpectativas incorrectas sobre MROPensar en cadena de módulos Odoo
Crear _name nuevo sin necesidadVistas, menús y ACL duplicadosExtender con _inherit solo

Práctica

Checklist antes de elegir

  1. ¿El modelo ya existe en el core o en otro módulo? → probablemente _inherit
  2. ¿Necesito una entidad nueva con identidad propia en menús/informes? → valora _inherits o modelo + Many2one
  3. ¿Los datos del padre deben vivir/v morir con el hijo? → _inherits con ondelete='cascade'
  4. ¿Solo añado campos y lógica? → _inherit sin dudar
  5. ¿Privacidad o separación de dominio? → modelo independiente + relación, no delegación

Resumen

_inherit extiende; _inherits delega. El 90% de los módulos custom solo necesitan _inherit. Reserva _inherits para composición real — como productos o usuarios — y cuando el modelo hijo tenga sentido de negocio propio. Si dudas, empieza con _inherit o un Many2one explícito: siempre podrás refactorizar con tests que te respalden.

en Odoo
TDD en módulos Odoo: desarrollo guiado por pruebas en Odoo 19
Red → Green → Refactor con TransactionCase, ejemplos reales y buenas prácticas