Ir al contenido

Wizards (TransientModel): flujos multi-paso bien diseñados

Estado temporal, validación y retorno de acciones al cerrar
11 de julio de 2026 por
Wizards (TransientModel): flujos multi-paso bien diseñados
Atemi, Aitor Atencia

Odoo · Wizards · UX

Un wizard mal diseñado se nota enseguida: pierde el estado entre pasos, no valida hasta el final, o deja registros a medias si el usuario cierra la ventana a mitad de camino. TransientModel resuelve el ciclo de vida de los datos temporales, pero el diseño del flujo sigue siendo responsabilidad tuya.

Logo Odoo
Un wizard es un modelo con fecha de caducidad: sus registros se purgan automáticamente.

La base

Por qué TransientModel y no Model

Un wizard no necesita persistir datos de negocio: solo recoge información temporal para ejecutar una acción. TransientModel hereda de Model pero sus registros se autoeliminan mediante un cron (vacuum), evitando que la tabla crezca sin control con cada apertura del asistente.

# wizards/export_wizard.py
class ExportWizard(models.TransientModel):
    _name = "mi_modulo.export.wizard"
    _description = "Asistente de exportación"

    date_from = fields.Date(required=True)
    date_to = fields.Date(required=True)
    partner_ids = fields.Many2many("res.partner", string="Clientes")

Flujos multi-paso

Mantener el estado entre pantallas con state

Cuando el wizard tiene varios pasos, el patrón habitual es un campo Selection que actúa como máquina de estados, combinado con una vista form que muestra secciones distintas según el paso activo mediante invisible.

state = fields.Selection([
    ("step1", "Selección de rango"),
    ("step2", "Revisión"),
    ("done", "Confirmado"),
], default="step1")

def action_next(self):
    self.ensure_one()
    self.state = "step2"
    return self._reopen_wizard()

def _reopen_wizard(self):
    return {
        "type": "ir.actions.act_window",
        "res_model": self._name,
        "res_id": self.id,
        "view_mode": "form",
        "target": "new",
    }

Cada botón del wizard invoca un método que valida el paso actual antes de avanzar, en vez de dejar toda la validación para el botón final.

El paso final

Devolver la acción correcta al cerrar

El método que ejecuta la acción de negocio debe devolver explícitamente qué hacer después: cerrar la ventana, abrir el registro creado, o mostrar una notificación. Dejar que Odoo decida por defecto suele producir una experiencia confusa.

def action_confirm(self):
    self.ensure_one()
    doc = self.env["mi_modulo.documento"].create(self._prepare_values())
    return {
        "type": "ir.actions.act_window",
        "res_model": "mi_modulo.documento",
        "res_id": doc.id,
        "view_mode": "form",
        "target": "current",
    }

Contexto

Pasar datos desde el modelo origen con default_get

Cuando el wizard se lanza desde un botón de un formulario (factura, pedido, tarea), conviene precargar campos usando el contexto de la acción en vez de pedirle al usuario que repita información que ya está en pantalla.

@api.model
def default_get(self, fields_list):
    res = super().default_get(fields_list)
    active_ids = self.env.context.get("active_ids", [])
    if "partner_ids" in fields_list and active_ids:
        res["partner_ids"] = [(6, 0, active_ids)]
    return res
NecesitasUsa
Recoger datos temporales sin persistirlosTransientModel
Varios pasos en el mismo asistenteCampo state + reapertura del wizard
Precargar datos del registro origendefault_get() + active_id/active_ids
Abrir el resultado tras confirmarir.actions.act_window con res_id

Resumen

TransientModel te libera de gestionar la limpieza de datos temporales, pero un buen wizard sigue exigiendo diseño: validar por paso, no acoplar modelos persistentes al wizard, y devolver siempre una acción explícita al terminar.

en Odoo
Secuencias, referencias XML y env.ref()
Evitar IDs hardcodeados y referencias rotas entre módulos