Práctica: Módulo viajes. Parte 3
Relacionamos vehículo, conductor, propietario y pasajeros. Las relaciones requieren campos inversos correctos y políticas de borrado explícitas.
1. Vehículo y sus viajes #
Sustituye el archivo del modelo de vehículo por esta versión:
Fichero models/vehiculo.py:
1from odoo import api, fields, models 2 3 4class Vehiculo(models.Model): 5 _name = 'viajes.vehiculo' 6 _description = 'Vehículo' 7 _rec_name = 'modelo' 8 9 modelo = fields.Char(required=True) 10 marca = fields.Char() 11 descripcion = fields.Text() 12 propietario_id = fields.Many2one('res.users', ondelete='set null', index=True) 13 viaje_ids = fields.One2many('viajes.viaje', 'vehiculo_id') 14 15 @api.depends('modelo', 'marca') 16 def _compute_display_name(self): 17 for record in self: 18 record.display_name = ' - '.join(filter(None, [record.modelo, record.marca]))
propietario_id referencia un usuario (res.users); crear usuarios requiere permisos de administración y no está garantizado por un campo Many2one. viaje_ids muestra la relación inversa del campo vehiculo_id del viaje. _compute_display_name combina modelo y marca, asignando siempre un texto.
2. Viaje con relaciones #
Antes de hacer obligatorio un nuevo campo en una base con registros, utiliza dos actualizaciones: añade temporalmente vehiculo_id sin required=True, actualiza y asigna vehículos a todos los viajes existentes; después aplica esta versión definitiva y vuelve a actualizar. No borres datos para resolver la migración.
Fichero models/viaje.py:
1from odoo import fields, models 2 3 4class Viaje(models.Model): 5 _name = 'viajes.viaje' 6 _description = 'Viaje' 7 _rec_name = 'titulo' 8 9 titulo = fields.Char(required=True) 10 fecha_inicio = fields.Date(default=fields.Date.context_today) 11 duracion = fields.Integer(default=0, help='Duración en días enteros') 12 plazas = fields.Integer(default=0, string='Capacidad de pasajeros') 13 estado = fields.Selection([ 14 ('planeado', 'Planeado'), ('en_curso', 'En curso'), 15 ('finalizado', 'Finalizado'), 16 ], default='planeado', required=True) 17 18 conductor_id = fields.Many2one('res.partner', ondelete='restrict') 19 vehiculo_id = fields.Many2one('viajes.vehiculo', required=True, ondelete='restrict') 20 pasajeros_ids = fields.Many2many( 21 'res.partner', 'viajes_pasajero_rel', 'viaje_id', 'partner_id', 22 string='Pasajeros', 23 )
Un vehículo con viajes no se puede borrar: usamos ondelete='restrict' para preservar su historial. La capacidad se refiere a pasajeros; el conductor es un rol separado. La relación de pasajeros tiene tabla y columnas explícitas para reutilizarla desde contactos en la siguiente lección.
3. Ampliar las vistas #
Añade views/relaciones.xml al manifiesto, después de las vistas base y antes de los menús:
Fichero views/relaciones.xml:
1<odoo> 2<record id="vehiculo_relaciones_form" model="ir.ui.view"> 3 <field name="name">vehiculo.relaciones.form</field> 4 <field name="model">viajes.vehiculo</field> 5 <field name="inherit_id" ref="vehiculo_form_view"/> 6 <field name="arch" type="xml"> 7 <xpath expr="//field[@name='marca']" position="after"><field name="propietario_id"/></xpath> 8 <xpath expr="//notebook" position="inside"> 9 <page string="Viajes"><field name="viaje_ids"> 10 <list><field name="titulo"/><field name="conductor_id"/></list> 11 </field></page> 12 </xpath> 13 </field> 14 </record> 15 <record id="viaje_relaciones_form" model="ir.ui.view"> 16 <field name="name">viaje.relaciones.form</field> 17 <field name="model">viajes.viaje</field> 18 <field name="inherit_id" ref="viaje_form_view"/> 19 <field name="arch" type="xml"> 20 <xpath expr="//group[@name='general']" position="inside"> 21 <field name="vehiculo_id"/><field name="conductor_id"/> 22 </xpath> 23 <xpath expr="//sheet" position="inside"> 24 <notebook name="detalles"> 25 <page string="Pasajeros"><field name="pasajeros_ids"/></page> 26 </notebook> 27 </xpath> 28 </field> 29 </record> 30 <record id="viaje_relaciones_list" model="ir.ui.view"> 31 <field name="name">viaje.relaciones.list</field> 32 <field name="model">viajes.viaje</field> 33 <field name="inherit_id" ref="viaje_list_view"/> 34 <field name="arch" type="xml"> 35 <xpath expr="//field[@name='estado']" position="after"><field name="vehiculo_id"/></xpath> 36 </field> 37 </record> 38</odoo>
Las vistas heredadas añaden campos y pestañas sin reemplazar el formulario anterior. Actualiza el módulo y comprueba: asignación de vehículo y conductor, varios pasajeros por viaje y varios viajes por vehículo. El filtrado de conductores se añadirá después de extender contactos.