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.
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.
sudo() debe poder explicarse en una frase de por qué ese bypass es intencional y seguro. Si no puedes, es un síntoma de una regla de acceso mal diseñada.
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ón | Alternativa |
|---|---|
| God model | Modelos relacionados + _inherit selectivo |
sudo() sin justificar | Regla de acceso o record rule correcta |
| Módulo monolito | Módulos pequeños con dependencias explícitas |
search() en bucle | read_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.