Odoo · Mixins · Arquitectura
En Odoo, los mixins son la forma más limpia de añadir comportamiento transversal sin inflar cada modelo. mail.thread trae chatter, mail.activity.mixin las actividades y utm.mixin el tracking de campañas. El error habitual: heredarlos todos «por si acaso» y violar el principio de responsabilidad única.
Concepto
¿Qué es un mixin en Odoo?
Un mixin es un modelo abstracto (_name con prefijo mail., utm., etc.) que aporta campos, métodos y vistas al heredarlo en tu modelo con _inherit:
class DiagnosticTest(models.Model):
_name = 'diagnostic.test'
_inherit = ['mail.thread', 'mail.activity.mixin']
_description = 'Diagnostic Test'
Odoo fusiona automáticamente campos como message_ids, activity_ids y la lógica asociada. No duplicas código: reutilizas infraestructura probada por millones de instalaciones.
SOLID aplicado: un mixin = una responsabilidad. Si tu modelo de catálogo interno no necesita historial de mensajes, no añadas
mail.thread.
mail.thread
mail.thread — chatter y seguimiento
| Aporta | Campos / métodos clave | Cuándo usarlo |
|---|---|---|
| Chatter en formulario | message_ids, message_follower_ids | Procesos con comunicación interna |
| Tracking de cambios | tracking=True en campos | Estados, asignaciones, fechas críticas |
| Notificaciones email | message_post, suscriptores | Flujos que avisan al equipo o cliente |
| Adjuntos en hilo | ir.attachment vinculados | Documentos asociados al registro |
class ProjectTask(models.Model):
_inherit = ['project.task', 'mail.thread']
state = fields.Selection(
selection=[('open', 'Open'), ('done', 'Done')],
tracking=True, # Aparece en el chatter automáticamente
)
def action_notify_team(self):
self.message_post(
body='Task reassigned. Please review.',
subtype_xmlid='mail.mt_comment',
)
mail.thread crea filas en mail_message y mail_followers. En modelos de alta frecuencia (líneas de pedido, movimientos de stock) el chatter puede degradar rendimiento y ensuciar la UI.
mail.activity.mixin
mail.activity.mixin — actividades y recordatorios
Añade el widget de actividades (llamadas, reuniones, tareas pendientes) y los métodos para programarlas:
class CrmLead(models.Model):
_inherit = ['crm.lead', 'mail.activity.mixin']
def action_schedule_followup(self):
self.activity_schedule(
'mail.mail_activity_data_call',
date_deadline=fields.Date.today() + timedelta(days=3),
summary='Follow up on proposal',
user_id=self.user_id.id,
)
Combinación habitual en CRM, proyectos y helpdesk. Requiere dependencia del módulo mail (casi siempre instalado).
mail.activity.mixin suele ir junto a mail.thread, pero técnicamente son independientes. Si solo necesitas actividades sin historial de mensajes, puedes usar solo el mixin de actividades (caso raro).
utm.mixin
utm.mixin — campañas y atribución
Añade campos de marketing UTM para rastrear el origen de oportunidades, pedidos o leads:
class SaleOrder(models.Model):
_inherit = ['sale.order', 'utm.mixin']
# Hereda: campaign_id, medium_id, source_id
| Campo | Modelo | Uso típico |
|---|---|---|
campaign_id | utm.campaign | Campaña de marketing (ej. «Black Friday 2026») |
medium_id | utm.medium | Canal (email, CPC, redes) |
source_id | utm.source | Origen concreto (newsletter, Google Ads) |
Úsalo en modelos que participan en embudos comerciales: crm.lead, sale.order, formularios web. En modelos puramente internos (configuración, catálogos técnicos) es ruido innecesario.
Decisión
¿Qué mixin necesito?
mail.thread
- Procesos con estados y equipo
- Auditoría de cambios visible
- Comunicación cliente/interna
mail.activity.mixin
- Flujos con tareas pendientes
- SLA y seguimiento comercial
- Recordatorios por usuario
utm.mixin
- Atribución de ventas/leads
- Integración website / marketing
- Reporting por campaña
Vistas
Habilitar chatter en la vista form
<record id="view_diagnostic_test_form" model="ir.ui.view">
<field name="name">diagnostic.test.form</field>
<field name="model">diagnostic.test</field>
<field name="arch" type="xml">
<form>
<sheet>...</sheet>
<chatter/>
</form>
</field>
</record>
Sin <chatter/> en la vista, los mixins de mail existen pero el usuario no ve el widget. Para actividades, el chatter las incluye automáticamente.
__manifest__.py: si usas mixins de mail, declara 'depends': ['mail'] (o un módulo que ya lo incluya, como sale). Sin la dependencia, la instalación fallará con modelos no encontrados.
Antipatrones
Cuándo no usar mixins
| Situación | Problema | Alternativa |
|---|---|---|
Modelo transitorio (TransientModel) | Chatter sin sentido en wizards | Sin mixins de mail |
| Tablas de configuración | Ruido en UI y BD | Log en ir.logging o modelo de auditoría dedicado |
| Alta cardinalidad (millones de filas) | mail_message explota | Tracking selectivo o modelo de eventos ligero |
| Copiar mixins «por costumbre» | Violación SRP, dependencias extra | Lista explícita en spec del módulo |
Resumen
mail.thread para comunicación y tracking, mail.activity.mixin para tareas programadas, utm.mixin para atribución de marketing. Hereda solo lo que el proceso de negocio necesita, declara dependencias en el manifest y añade <chatter/> en la vista. Menos mixins = modelos más rápidos, más legibles y más alineados con SOLID.