Ir al contenido

Antipatrones en proyectos Odoo (top 10)

God models, sudo() everywhere, lógica en XML, módulos monolito
11 de julio de 2026 por
Antipatrones en proyectos Odoo (top 10)
Atemi, Aitor Atencia

Odoo · Arquitectura · Opinión

Los mismos diez errores aparecen una y otra vez en proyectos Odoo de años de antigüedad. Ninguno rompe nada el primer día; todos pasan factura en el tercer año de mantenimiento, cuando tocar un modelo se convierte en un acto de fe.

Logo Odoo
Los antipatrones no se ven en el commit que los introduce, sino dos años después.

Modelos

1-3. God models, herencia por copia y lógica de negocio en XML

El god model es el modelo con 80 campos y 40 métodos que "hace de todo" porque nadie quiso crear un modelo relacionado nuevo. La herencia por copia es duplicar un modelo OCA o de core en vez de usar _inherit, perdiendo todas las actualizaciones futuras. Y la lógica de negocio en XML son los domain y attrs kilométricos que deberían ser un método Python testeable.

# Mal: la regla de negocio vive escondida en un domain XML
<field name="partner_id"
       domain="[('customer_rank','>',0),('country_id','=',1),('category_id','in',[3,7,12])]"/>

# Bien: expuesta como método, testeable y reutilizable
domain = self._get_eligible_partner_domain()

Seguridad y permisos

4-6. sudo() everywhere, ACL copiadas y validación solo en frontend

sudo() añadido "porque daba error de permisos" sin entender por qué, en vez de corregir la regla de acceso real, es el antipatrón de seguridad más común. Le sigue de cerca copiar-pegar ir.model.access.csv de otro modelo sin ajustar los grupos, y confiar en que el JavaScript del formulario evitará que se guarden datos inválidos.

Estructura y despliegue

7-8. Módulos monolito y datos demo mezclados con datos base

Un módulo único de 15.000 líneas que mezcla ventas, RRHH y contabilidad porque "así es más fácil instalar" hace imposible reutilizar partes o testear en aislamiento. Y mezclar datos de demo/ con datos base en el mismo fichero XML provoca que un -i en producción cargue registros de prueba por error.

# __manifest__.py: separación correcta
"data": ["data/ir_sequence.xml", "views/mi_modelo_views.xml"],
"demo": ["demo/mi_modelo_demo.xml"],

Rendimiento y calidad

9-10. Bucles con search() anidado y cero tests

Un for record in self: record.otro_modelo.search([...]) dentro de un compute es una consulta N+1 esperando a explotar con volumen real. Y la ausencia total de tests significa que cada actualización de módulo es, en la práctica, una prueba en producción.

AntipatrónAlternativa
God modelModelos relacionados + _inherit selectivo
sudo() sin justificarRegla de acceso o record rule correcta
Módulo monolitoMódulos pequeños con dependencias explícitas
search() en bucleread_group / prefetch / una sola consulta

Resumen

Ninguno de estos diez antipatrones impide que el módulo funcione hoy. Todos comparten el mismo coste oculto: convierten el mantenimiento futuro en arqueología. Detectarlos en code review es mucho más barato que refactorizarlos con el cliente ya en producción.

en Odoo
OCA vs módulo propio: cuándo contribuir y cuándo forkar
Criterios de decisión para proyectos reales