Odoo · Módulos · Datos
"Funcionaba en mi máquina" tiene una variante muy odoica: "se instaló bien la primera vez, pero al actualizar el módulo se ha duplicado todo". Casi siempre es un problema de noupdate, de orden de carga o de un ID mal referenciado en los ficheros de datos.
La opción por defecto
noupdate="1": protege los datos frente a futuras actualizaciones
Cuando un fichero XML se marca con noupdate="1", sus registros se crean en la instalación pero no se vuelven a tocar en actualizaciones posteriores del módulo, aunque el XML cambie en el código. Es el comportamiento correcto para datos que el usuario puede personalizar después: secuencias, plantillas de email, datos demo.
<odoo>
<data noupdate="1">
<record id="mail_template_bienvenida" model="mail.template">
<field name="name">Bienvenida cliente</field>
<field name="subject">Bienvenido/a</field>
</record>
</data>
</odoo>
noupdate="1" tras publicarlo, cambiar el XML no sirve de nada en las bases de datos ya instaladas. Hace falta un script de migración que actualice el registro explícitamente vía ORM.
Lo que sí se actualiza siempre
Vistas, menús y acciones: sin noupdate
Las vistas, menús, grupos de seguridad y acciones normalmente van sin noupdate="1" (o con noupdate="0", el valor por defecto), precisamente porque queremos que un -u las actualice cuando cambian en el código. Mezclar ambos tipos de datos en el mismo fichero es la fuente más común de confusión.
<odoo>
<!-- Sin noupdate: se actualiza en cada -u -->
<record id="view_mi_modelo_form" model="ir.ui.view">
<field name="name">mi.modelo.form</field>
<field name="model">mi.modelo</field>
<field name="arch" type="xml">
<form><field name="name"/></form>
</field>
</record>
</odoo>
El orden importa
El manifest carga los ficheros en orden, no alfabético
Odoo procesa la lista data del __manifest__.py en el orden literal en que aparece. Si un registro referencia (vía ref o env.ref()) un ID que todavía no se ha creado, la instalación falla con un ValueError: External ID not found.
| Orden recomendado | Motivo |
|---|---|
1. security/*.csv y grupos | Las vistas y menús suelen referenciar grupos de seguridad |
2. data/*.xml (secuencias, datos base) | Los registros de negocio pueden depender de ellos |
3. views/*.xml | Referencian modelos y grupos ya cargados |
4. data/mail_template_data.xml, demo/*.xml | Suelen referenciar vistas o plantillas previas |
ref="modulo.id_de_A", el fichero A tiene que aparecer antes que B en la lista data del manifest, sin excepciones.
Cuando el volumen es grande
CSV como shortcut para datos masivos
Para catálogos grandes (códigos postales, categorías, datos maestros con cientos de filas), un CSV es mucho más legible que XML repetitivo. La primera columna siempre es id (el external ID) y el resto son los nombres técnicos de los campos.
# data/res_partner_category.csv
id,name,color
categoria_vip,Cliente VIP,1
categoria_moroso,Moroso,2
categoria_nuevo,Nuevo,5
# __manifest__.py
"data": [
"security/ir.model.access.csv",
"data/res_partner_category.csv",
"views/res_partner_views.xml",
],
Idempotencia real
Por qué reinstalar no debería duplicar nada
El external ID (id en XML/CSV) es lo que garantiza idempotencia: si el registro ya existe con ese ID, Odoo lo actualiza (o lo ignora si está en noupdate); si no existe, lo crea. El problema aparece cuando un desarrollador crea datos vía código Python (create() en un hook de instalación) sin comprobar antes si ya existen, generando duplicados en cada reinstalación.
# Mal: duplica en cada reinstalación
def post_init_hook(env):
env["res.partner.category"].create({"name": "Cliente VIP"})
# Bien: idempotente, usa el external ID
def post_init_hook(env):
env["res.partner.category"]._load_records([{
"xml_id": "mi_modulo.categoria_vip",
"values": {"name": "Cliente VIP"},
"noupdate": True,
}])
Resumen
La mayoría de fallos de instalación y duplicados en Odoo se explican por tres cosas: usar noupdate="1" donde toca (y no donde no toca), respetar el orden de carga del manifest para que los ref siempre encuentren su objetivo, y usar external IDs también desde Python cuando se crean datos por código.