5. Desarrollando con Odoo (3). Relaciones entre modelos , atributos de campos y campos calculados. Menu sequence. No confundir date con datetime
1. Añadir información a los campos del modelo.
Al campo name le hemos añadido estos atributos:
- string : Etiqueta del campo
- readonly: Si es de solo lectura (no modificable)
- required: Campo obligatorio
- help: Información "tool tip"
- default: Valor por omisión
class task(models.Model): _name = 'ximoapp01.task' _description = 'ximoapp01.task' name = fields.Char(string= "Nombre", readonly=False, required=True, help="Nombre de la Tarea", default="Crear índices" )
Hay diferentes tipos de campos:
- fields.Char() : tira de caràcteres
- fields.Text(): Texto más grande
- fields.Date(): Fecha
- fields.Boolean(): Booleano
- fields.Integer(): Entero
- fields:Binary(): Un fichero, por ejemplo un documento
- fields.Image(): Imagen (subtipo de Binary)
- fields.Monetary(): Moneda
- fields.Html(): HTML
- fields.Selection(): Conjunto de valores posibles
2. Campos calculados
Veamos dos ejemplos, el primero no se guarda en la BD y el segundo si.
from odoo import models, fields, api import datetime class task(models.Model): _name = 'ximoapp01.task' _description = 'ximoapp01.task' name = fields.Char(string= "Nombre", readonly=False, required=True, help="Nombre de la Tarea" ) description = fields.Text() creation_date = fields.Datetime() start_date = fields.Datetime() duration= fields.Integer() end_date =fields.Datetime(compute="_get_end_date", store=True ) is_paused = fields.Boolean(default=False) code = fields.Char(compute="_get_code") def _get_code(self): for record in self: record.code=record.name + str(record.id) @api.depends('start_date', 'duration') def _get_end_date(self): for record in self: if isinstance(record.start_date, datetime.datetime): and record.duration > 0 :
record.end_date = record.start_date + datetime.timedelta(days=record.duration) else: record.end_date=record.start_date
El primer ejemplo crea un campo "code" que toma el valor del campo "name" y le añadimos el valo del campo "id" convertido en tira. No se guarda en la BD pues si se guardara se indicaría con el atributo store = True
En este primer caso se ha definido una función "_get_code" dentro del modelo que genera el valor calculado del campo
En el segundo ejemplo, el campo calculado (end_date) se guarda en la BD (store = True) y además depende de dos campos (start_date y duration), por eso la función que calcula su valor (_get_end_date), tiene el decorador @api.depends('start_date','duration')
Hay que destacar en la función "_det_end_date" 2 aspectos:
- Si el campo de tipo datetime no tiene valor, entonces toma el valor "False" y hay que comprobr que el valor que tiene es tipo datetime.datetime.
- Para poder añadir dias a una fecha se debe utilizar datetime.timedelta(days=dias_a_sumar)
OJO: Si creamos los campos como fields.Date, estos seran del tipo datetime.date, y si los creamos como fileds.Datetime estos serán del tipo datetime.datetime
3. Relación One2Many y Many2One
La primera relación se indica de un padre a sus hijos y la segunda de los hijos al padre. Vamos a seguir las indicaciones de Inma Gijón y crearemos un nuevo modelo llamado "Sprint" que representa una macrotarea que engloba varias tareas "Task".
Cada vez que creemos un modelo, hay que crear 5 elementos, 4 dentro del fichero views.xml y uno dentro de ir.model.access.csv :
- Crear las vistas tipo "tree" (list)(1) y opcionalmente la de tipo "form"(2), así como la acción(3) y el menú (4) dentro de views/views.xml
- Añadir una línea de permisos (5) al nuevo modelo dentro de security/ir.model.access.csv
Veamos el fichero models.py que contiene a Task y Sprint
from odoo import models, fields, api import datetime class task(models.Model): _name = 'ximoapp01.task' _description = 'ximoapp01.task' name = fields.Char(string= "Nombre", readonly=False, required=True, help="Nombre de la Tarea" ) description = fields.Text() creation_date = fields.Datetime() start_date = fields.Datetime()
duration= fields.Integer() end_date =fields.Datetime(compute="_get_end_date", store=True )
is_paused = fields.Boolean(default=False) code = fields.Char(compute="_get_code") sprint = fields.Many2one("ximoapp01.sprint", ondelete="set null", help="Macro tarea padre Sprint"))
def _get_code(self): for record in self: record.code=record.name + str(record.id) @api.depends('start_date', 'duration') def _get_end_date(self): for record in self: #if isinstance(record.start_date, datetime.datetime) and record.duration > 0 : # record.end_date = record.start_date + datetime.timedelta(days=record.duration) #if isinstance(record.start_date, datetime.datetime) and record.duration > 0: if not record.start_date==False: record.end_date = record.start_date + datetime.timedelta(days=record.duration)
else: record.end_date=record.start_date class sprint(models.Model): _name = 'ximoapp01.sprint' _description = 'ximoapp01.sprint' name = fields.Char(string= "Nombre", readonly=False, required=True, help="Nombre de la macro tarea Sprint" ) description = fields.Text()
creation_date = fields.Datetime()start_date = fields.Datetime()
duration= fields.Integer() end_date =fields.Datetime(compute="_get_end_date", store=True )
#tasks = fields.One2many("ximoapp01.task", 'sprint') # >Le pasamos modelo y campo de la tabla hija que hace referencia al padre tasks = fields.One2many(string="Tareas", comodel_name="ximoapp01.task", inverse_name='sprint') # >Le pasamos modelo y campo de la tabla hija que hace referencia al padre
@api.depends('start_date', 'duration') def _get_end_date(self): for record in self: #if isinstance(record.start_date, datetime.datetime) and record.duration > 0 : # record.end_date = record.start_date + datetime.timedelta(days=record.duration) #if isinstance(record.start_date, datetime.datetime) and record.duration > 0: if not record.start_date==False: record.end_date = record.start_date + datetime.timedelta(days=record.duration) else: record.end_date=record.start_date
En la relación Many2one solo le indicamos el modelo padre mientras que en la relación One2many hay que indicarle el modelo hijo y el campo del modelo hijo que hace referencia al padre.
Como vimos, en el fichero views.xml hay que añadirle los formularios, acciones y menús
<odoo> <data> <!-- view T A S K --> <!-- explicit list view definition --> <record model="ir.ui.view" id="ximoapp01.task_list"> <field name="name">ximoapp01 task list</field> <field name="model">ximoapp01.task</field> <field name="arch" type="xml"> <tree> <field name="name"/> <field name="description"/> <field name="creation_date"/> <field name="start_date"/> <field name="duration"/> <field name="end_date"/> <field name="is_paused"/> <field name="code"/> <field name="sprint"/> </tree> </field> </record> <!-- explicit form view definition --> <record model="ir.ui.view" id="ximoapp01.task_form"> <field name="name">ximoapp01 task form</field> <field name="model">ximoapp01.task</field> <field name="arch" type="xml"> <form> <group> <field name="name"/> <field name="description"/> <field name="creation_date"/> <field name="start_date"/> <field name="duration"/> <field name="end_date"/> <field name="is_paused"/> <field name="code"/> <field name="sprint"/> </group> </form> </field> </record> <!-- view S P R I N T --> <!-- explicit list view definition --> <record model="ir.ui.view" id="ximoapp01.sprint_list"> <field name="name">ximoapp01 sprint list</field> <field name="model">ximoapp01.sprint</field> <field name="arch" type="xml"> <tree> <field name="name"/> <field name="description"/> <field name="creation_date"/> <field name="start_date"/> <field name="duration"/> <field name="end_date"/> </tree> </field> </record> <!-- explicit form view definition --> <record model="ir.ui.view" id="ximoapp01.sprint_form"> <field name="name">ximoapp01 sprint form</field> <field name="model">ximoapp01.sprint</field> <field name="arch" type="xml"> <form> <group> <field name="name"/> <field name="description"/> <field name="creation_date"/> <field name="start_date"/> <field name="duration"/> <field name="end_date"/> <field name="tasks"/> </group> </form> </field> </record> <!-- actions opening views on models --> <!-- action T A S K -->
<record model="ir.actions.act_window" id="ximoapp01.action_task_window"> <field name="name">ximoapp01 task window</field> <field name="res_model">ximoapp01.task</field> <field name="view_mode">tree,form</field> </record>
<!-- action S P R I N T --><record model="ir.actions.act_window" id="ximoapp01.action_sprint_window"> <field name="name">ximoapp01 sprint window</field> <field name="res_model">ximoapp01.sprint</field> <field name="view_mode">tree,form</field> </record> <!-- server action to the one above --> <!-- <record model="ir.actions.server" id="ximoapp01.action_server"> <field name="name">ximoapp01 server</field> <field name="model_id" ref="model_ximoapp01_ximoapp01"/> <field name="state">code</field> <field name="code"> action = { "type": "ir.actions.act_window", "view_mode": "tree,form", "res_model": model._name, } </field> </record> --> <!-- Top menu item --> <menuitem name="ximoapp01" id="ximoapp01.menu_root"/> <!-- menu categories --> <menuitem name="Tasks & Sprints" id="ximoapp01.menu_1" parent="ximoapp01.menu_root"/> <!-- <menuitem name="Menu_2" id="ximoapp01.menu_2" parent="ximoapp01.menu_root"/> --> <!-- actions -->
<!-- 1. T A S K S -->
<menuitem name="Tasks" id="ximoapp01.menu_1_task_list" parent="ximoapp01.menu_1" action="ximoapp01.action_task_window"/>
<!-- 2. S P R I N T S --> <menuitem name="Sprints" id="ximoapp01.menu_1_sprint_list" parent="ximoapp01.menu_1" action="ximoapp01.action_sprint_window"/> </data> </odoo>
Y el fichero security/ir.model.access.csv queda:
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_ximoapp01_task,ximoapp01.task,model_ximoapp01_task,base.group_user,1,1,1,1 access_ximoapp01_sprint,ximoapp01.sprint,model_ximoapp01_sprint,base.group_user,1,1,1,1
4. Relación Many2many
Vamos a crear un tercer modelo llamado "technology", que indica las tecnologías utilizadas para realizar una tarea (por ejemplo Python, Postgres, Linux, Windows, shell, cron, .. )
Veamos la línea a añadir en el modelo task para que tenga el campo tecnologies del model technology
class task(models.Model):
- - - - - - - - technologies = fields.Many2many(comodel_name="ximoapp01.technology",
relation="technology_task",
column1="task_id",
column2="technology_id",
help="Tecnologías empleadas")
Y para el modelo techology:
class technology(models.Model):
- - - - - - - - tasks = fields.Many2many(comodel_name="ximoapp01.task",
relation="technology_task",
column1="technology_id",
column2="task_id",
help="Se emplea en estas tareas")
Donde:
- comodel_name es el modelo con el que se relaciona
- relation: nombre de la tabla que guardara dicha relación (debe ser la misma)
- column1: Nombre del campo que hace referencia al modelo actual
- column2: Nombre del campo que hace referencia al modelo relacionado. Observar que esos campos (column1 y column2) se intercambian en las relaciones.
Veamos pues como quedan los ficheros afectados:
4.1 Models.py
from odoo import models, fields, api import datetime class task(models.Model): _name = 'ximoapp01.task' _description = 'ximoapp01.task' name = fields.Char(string= "Nombre", readonly=False, required=True, help="Nombre de la Tarea" ) description = fields.Text() creation_date = fields.Datetime()
start_date = fields.Datetime()
duration= fields.Integer() end_date =fields.Datetime(compute="_get_end_date", store=True )
is_paused = fields.Boolean(default=False) code = fields.Char(compute="_get_code") sprint = fields.Many2one("ximoapp01.sprint", ondelete="set null",help="Macro tarea padre Sprint") technologies = fields.Many2many(comodel_name="ximoapp01.technology", relation="technology_task", column1="task_id", column2="technology_id", help="Tecnologías empleadas") def _get_code(self): for record in self: record.code=record.name + str(record.id) @api.depends('start_date', 'duration') def _get_end_date(self): for record in self:
if isinstance(sprint.end_date, datetime.datetime) and record.duration > 0 ::record.end_date = record.start_date + datetime.timedelta(days=record.duration) else: record.end_date=record.start_date class sprint(models.Model): _name = 'ximoapp01.sprint' _description = 'ximoapp01.sprint' name = fields.Char(string= "Nombre", readonly=False, required=True, help="Nombre de la macro tarea Sprint" ) description = fields.Text() creation_date = fields.Datetime() start_date = fields.Datetime()
duration= fields.Integer() end_date =fields.Datetime(compute="_get_end_date", store=True )
tasks = fields.One2many(string="Tareas", comodel_name="ximoapp01.task", inverse_name='sprint') # >Le pasamos modelo y campo de la tabla hija que hace referencia al padre @api.depends('start_date', 'duration') def _get_end_date(self): for record in self: for record in self:class technology(models.Model): _name = 'ximoapp01.technology' _description = 'ximoapp01.technology' name = fields.Char(string= "Nombre", readonly=False, required=True, help="Nombre de la Tecnología" ) description = fields.Text() photo = fields.Image(max_width=200, max_heigh=200) tasks = fields.Many2many(comodel_name="ximoapp01.task", relation="technology_task", column1="technology_id", column2="task_id", help="Tareas donde se emplea")if isinstance(sprint.end_date, datetime.datetime) and record.duration > 0 ::record.end_date = record.start_date + datetime.timedelta(days=record.duration) else: record.end_date=record.start_date
4.2 Views.xml
<odoo> <data> <!-- T A S K --> <!-- explicit list view definition --> <record model="ir.ui.view" id="ximoapp01.task_list"> <field name="name">ximoapp01 task list</field> <field name="model">ximoapp01.task</field> <field name="arch" type="xml"> <tree> <field name="name"/> <field name="description"/> <field name="creation_date"/> <field name="start_date"/> <field name="duration"/> <field name="end_date"/> <field name="is_paused"/> <field name="sprint"/> <field name="technologies"/> </tree> </field> </record> <!-- explicit form view definition --> <record model="ir.ui.view" id="ximoapp01.task_form"> <field name="name">ximoapp01 task form</field> <field name="model">ximoapp01.task</field> <field name="arch" type="xml"> <form> <group> <field name="name"/> <field name="description"/> <field name="creation_date"/> <field name="start_date"/> <field name="duration"/> <field name="end_date"/> <field name="is_paused"/> <field name="sprint"/> <field name="technologies"/> </group> </form> </field> </record> <!-- S P R I N T --> <!-- explicit list view definition --> <record model="ir.ui.view" id="ximoapp01.sprint_list"> <field name="name">ximoapp01 sprint list</field> <field name="model">ximoapp01.sprint</field> <field name="arch" type="xml"> <tree> <field name="name"/> <field name="description"/> <field name="creation_date"/> <field name="start_date"/> <field name="duration"/> <field name="end_date"/> <field name="tasks"/> </tree> </field> </record> <!-- explicit form view definition --> <record model="ir.ui.view" id="ximoapp01.sprint_form"> <field name="name">ximoapp01 sprint form</field> <field name="model">ximoapp01.sprint</field> <field name="arch" type="xml"> <form> <group> <field name="name"/> <field name="description"/> <field name="creation_date"/> <field name="start_date"/> <field name="duration"/> <field name="end_date"/> <field name="tasks"/> </group> </form> </field> </record> <!-- T E C H N O L O G Y --> <!-- explicit list view definition --> <record model="ir.ui.view" id="ximoapp01.sprint_list"> <field name="name">ximoapp01 technology list</field> <field name="model">ximoapp01.technology</field> <field name="arch" type="xml"> <tree> <field name="name"/> <field name="description"/> <field name="photo"/> <field name="tasks"/> </tree> </field> </record> <!-- explicit form view definition --> <record model="ir.ui.view" id="ximoapp01.sprint_form"> <field name="name">ximoapp01 sprint form</field> <field name="model">ximoapp01.sprint</field> <field name="arch" type="xml"> <form> <group> <field name="name"/> <field name="description"/> <field name="creation_date"/> <field name="start_date"/> <field name="duration"/> <field name="end_date"/> <field name="tasks"/> </group> </form> </field> </record> <!-- actions opening views on models --> <!-- T A S K --> <record model="ir.actions.act_window" id="ximoapp01.action_task_window"> <field name="name">ximoapp01 task window</field> <field name="res_model">ximoapp01.task</field> <field name="view_mode">tree,form</field> </record> <!-- S P R I N T --> <record model="ir.actions.act_window" id="ximoapp01.action_sprint_window"> <field name="name">ximoapp01 sprint window</field> <field name="res_model">ximoapp01.sprint</field> <field name="view_mode">tree,form</field> </record> <!-- T E C H N O L O G Y --> <record model="ir.actions.act_window" id="ximoapp01.action_technology_window"> <field name="name">ximoapp01 technology window</field> <field name="res_model">ximoapp01.technology</field> <field name="view_mode">tree,form</field> </record> <!-- server action to the one above --> <!-- <record model="ir.actions.server" id="ximoapp01.action_server"> <field name="name">ximoapp01 server</field> <field name="model_id" ref="model_ximoapp01_ximoapp01"/> <field name="state">code</field> <field name="code"> action = { "type": "ir.actions.act_window", "view_mode": "tree,form", "res_model": model._name, } </field> </record> --> <!-- Top menu item --> <menuitem name="ximoapp01" id="ximoapp01.menu_root"/> <!-- menu categories --> <menuitem name="Tasks & Sprints" id="ximoapp01.menu_1" parent="ximoapp01.menu_root"/> <!-- <menuitem name="Menu 2" id="ximoapp01.menu_2" parent="ximoapp01.menu_root"/> --> <!-- actions --> <menuitem name="Tasks" id="ximoapp01.menu_1_task_list" parent="ximoapp01.menu_1" action="ximoapp01.action_task_window" sequence="10"/>
<menuitem name="Sprints" id="ximoapp01.menu_1_sprint_list" parent="ximoapp01.menu_1" action="ximoapp01.action_sprint_window" sequence="15"/>
<menuitem name="Technologies" id="ximoapp01.menu_1_technology_list" parent="ximoapp01.menu_1" action="ximoapp01.action_technology_window"/> <!-- <menuitem name="Server to list" id="ximoapp01" parent="ximoapp01.menu_2" action="ximoapp01.action_server"/> --> </data> </odoo>
Si queremos establecer la secuencia de aparición de los menús y submenús, utilizamos sequence
4.3 ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_ximoapp01_task,ximoapp01.task,model_ximoapp01_task,base.group_user,1,1,1,1 access_ximoapp01_sprint,ximoapp01.sprint,model_ximoapp01_sprint,base.group_user,1,1,1,1 access_ximoapp01_technology,ximoapp01.technology,model_ximoapp01_technology,base.group_user,1,1,1,1
Comentarios
Publicar un comentario