Odoo · Arquitectura · Convenciones
Todos los módulos empiezan pequeños: un modelo, una vista, un CSV de permisos. El problema llega en la versión 1.8, cuando hay quince modelos en un único models.py, un views.xml de dos mil líneas y nadie se atreve a tocar nada. La estructura no es burocracia: es lo que permite que alguien nuevo encuentre las cosas sin preguntarte.
Estructura
El árbol de directorios que todo el mundo espera
Odoo no obliga a ninguna estructura, pero la comunidad (ver convenciones de la comunidad) ha convergido en una. Seguirla tiene una ventaja enorme: cualquier desarrollador Odoo sabe dónde buscar sin leer tu README.
mi_modulo/
├── __init__.py
├── __manifest__.py
├── README.md
├── controllers/ # rutas HTTP
├── data/ # secuencias, crons, plantillas de email
├── demo/ # datos de demostración
├── i18n/ # .pot y .po
├── migrations/
│ └── 19.0.1.2.0/ # pre-, post- y end-migrate.py
├── models/
│ ├── __init__.py
│ ├── fleet_trip.py # un modelo por fichero
│ └── res_partner.py # extensiones: nombre del modelo heredado
├── report/ # acciones y plantillas QWeb de informes
├── security/
│ ├── security.xml # grupos y record rules
│ └── ir.model.access.csv
├── static/src/ # OWL, SCSS, JS
├── tests/
├── views/
│ ├── fleet_trip_views.xml
│ └── menus.xml
└── wizards/ # TransientModel + su vista
La regla que más rinde es la más simple: un modelo por fichero Python y un fichero de vistas por modelo, con el mismo nombre. Cuando un diff toca fleet_trip.py y fleet_trip_views.xml, el revisor sabe exactamente qué está cambiando.
Manifest
El orden de data importa
Odoo carga los ficheros de data en el orden exacto en que los listas. Si una vista referencia un grupo que se define después, la instalación falla. El orden que nunca da problemas es: seguridad, datos, vistas, wizards, informes y, al final, los menús (que referencian acciones definidas en las vistas).
{
"name": "Fleet Trips",
"version": "19.0.1.2.0",
"license": "LGPL-3",
"depends": ["fleet", "mail"],
"data": [
"security/security.xml",
"security/ir.model.access.csv",
"data/ir_sequence_data.xml",
"data/ir_cron_data.xml",
"views/fleet_trip_views.xml",
"wizards/fleet_trip_close_wizard_views.xml",
"report/fleet_trip_report.xml",
"views/menus.xml",
],
"demo": ["demo/fleet_trip_demo.xml"],
}
security.xml antes que el CSV: si ir.model.access.csv asigna permisos a un grupo propio, el grupo tiene que existir ya cuando se carga el CSV. Invertir el orden da un error de referencia externa que no siempre se ve en el primer -i sobre una base de datos que ya tenía el grupo.
Nombres
IDs XML predecibles
Los IDs XML son la API pública de tu módulo: otros módulos heredan tus vistas por ID y tus migraciones dependen de ellos. Cambiar un ID más adelante obliga a escribir una migración, así que merece la pena acertar a la primera con un patrón fijo.
| Elemento | Patrón | Ejemplo |
|---|---|---|
| Vista | <modelo>_view_<tipo> | fleet_trip_view_form |
| Herencia de vista | <modelo>_view_<tipo>_inherit_<módulo> | res_partner_view_form_inherit_fleet_trip |
| Acción | <modelo>_action | fleet_trip_action |
| Menú | <modelo>_menu | fleet_trip_menu |
| Grupo | group_<módulo>_<rol> | group_fleet_trip_manager |
| Record rule | <modelo>_rule_<alcance> | fleet_trip_rule_own |
Escalar
Cuándo partir un módulo en dos
La señal más clara es la lista de depends: si tu módulo de viajes depende de website solo porque tiene una página pública opcional, estás obligando a instalar el website a todo el que quiera gestionar viajes. La solución es un módulo núcleo y módulos puente pequeños, cada uno con la dependencia extra, marcados con auto_install para que se instalen solos cuando sus dependencias estén presentes.
# fleet_trip_website/__manifest__.py
{
"name": "Fleet Trips - Website",
"version": "19.0.1.0.0",
"depends": ["fleet_trip", "website"],
"auto_install": True, # se instala solo si fleet_trip y website lo están
"data": ["views/fleet_trip_templates.xml"],
}
En nuestro propio repositorio seguimos este patrón para las notificaciones: un módulo central de Telegram y un puente pequeño por cada módulo que quiere enviar avisos. Ningún módulo de negocio depende de Telegram, y desinstalar un puente no rompe nada más.
Resumen
Usa la estructura de directorios estándar, un modelo por fichero y un XML de vistas por modelo; ordena data como seguridad → datos → vistas → menús; elige IDs XML con un patrón fijo porque son tu API pública; y cuando una dependencia solo la necesita una parte del módulo, sácala a un módulo puente con auto_install.