7. Desarrollando con Odoo (5). Campos relacionales calculados. Valor por defecto calculado. Funciones lambda

1. Introducción

Vamos a añadir 2 modelos nuevos project y history. Vamos amostrar de forma un poco laxa como estan relacionados. Se indica en la parte superior el padre y en la inferior los hijos

project

|--> sprint

     |--> task

|--> history

     |--> task 

Podemos ver que de project cuelgan directamente sprint y history y que task tiene de padres a sprint y history.

También, como se indica en el video, vamos a eliminar los campos creation_date ya que por omisión, odoo incluye campos equivalentes.

2. Campo relacionado calculado Many2one

Las nuevas reglas del juego son:

  1. Solo habrá un sprint abierto por proyecto. El sprint abierto se caracteriza por tener el end_date mayor a la fecha de hoy
  2. A la tarea se le asignará un sprint abierto
  3. Para poder seleccionar el sprint, priomeramente se seleccionará un history padre, el cual nos apuntará a un proyecto, y como solo hay un único sprint abierto por proyecto, se seleccionará este como padre de la tarea.
  4. Una vez asignado el sprint, no se podrá cambiar. O sea solamente se puede asignar una vez
De entre todos los sprints que pueden ser padres de un task, vamos a elegir aquel que esté abierto en un proyecto. Solo habrá un sprint abierto por proyecto.

Para ver como se asigna el sprint y no volverlo a cambiar se puede hacer que dependa del campo "id" (@api_depends('id')).... Pero si metemos este decorador, ODOO NO ARRANCA!

Para ello se puede hacer un truco y es crear un campo calculado que coja el valor de id y que no se guarde en la BD, y meter el decorador @api_depends sobre dicho campo calculado

Ojo: lo que vamos a hacer es que el campo sprint dependa de history, con lo que si cambiamos el history, afectará al campo sprint y se acaba el problema 

Veamos resumidamente lo que hay que hacer:
  1. En el modelo de task, hacer el campo sprint calculado aplicandole una función de cálculo _get_sprint()
  2. En la función de cálculo utilizar la api de búsqueda de los registros de un  modelo  self.env['ximoapp01.sprint'].search([condicion])

class task(models.Model):
    _name = 'ximoapp01.task'
    --------------
    history = fields.Many2one("ximoapp01.history", ondelete="set null",help="Historia relacionada")
    sprint = fields.Many2one("ximoapp01.sprint", compute="_get_sprint", ondelete="set null", store= True, help="Macro tarea padre Sprint")
    
    
    @api.depends('history')
    def _get_sprint(self):
        for record in self:
            sprints = self.env['ximoapp01.sprint'].search([('project.id', '=', record.history.project.id)])
            found = False
            for sprint in sprints:
                if not found:
                     if isinstance(sprint.end_date, datetime.datetime):
                        if sprint.end_date > datetime.datetime.now():
                            record.sprint = sprint.id
                            found = True
            if not found:
                record.sprint = False


3. Campo relacionado calculado Many2many

Este caso es muy parecido al anterior. 

Ahora vamos a asignar al modelo history todas la tecnologías utilizadas en cada una de la tareas que dependen de esta history sin duplicar ninguna.

Para ello:
  1. Le añadimos el atributo compute que apunta al método que calcula el valor del campo
  2. En dicha función de cálculo recorremos las task de la history y añadimos a la lista las technology

veamos el trozo de código que nos interesa del modelo 

class history(models.Model):
    _name = 'ximoapp01.history'
    _description = 'ximoapp01.history'

    name = fields.Char(string= "Nombre", readonly=False, required=True, help="Nombre del Projecto" )
    description = fields.Text()
    project = fields.Many2one("ximoapp01.project", ondelete="set null", help="Proyecto")
    tasks = fields.One2many(string="Tareas", comodel_name="ximoapp01.task", inverse_name='history')
    technologies = fields.Many2many(comodel_name="ximoapp01.technology", compute="_get_technologies")
    
    def _get_technologies(self):
        for record in self:
            technologies = None
            for task in record.tasks:
                if not technologies:
                    technologies= task.technologies
                else:
                    technologies = technologies + task.technologies    
            record.technologies=technologies            


4. Campo por defecto calculado. Funciones lambda

En este caso hay que dar a default el valor de la función que lo calcula sin entrecolimmar, pero primeramente debemos definir la función que realiza el cálculo del valor por defecto antes de declarar el campo!!!

Veamos el trozo de código que nos interesa

class task(models.Model):
    _name = 'ximoapp01.task'
    --------
    
    def _get_definition_date(self):
        return datetime.datetime.now()
    
    definition_date = fields.Datetime(default=_get_definition_date)



Pero también podemos hacer uso de una función lambda, transformando 2 declaraciones separadas en una sola. Se ha comentado el código anterior 

class task(models.Model):
    _name = 'ximoapp01.task'
    --------
    
    #def _get_definition_date(self):
    #    return datetime.datetime.now()
    #definition_date = fields.Datetime(default=_get_definition_date)

    definition_date = fields.Datetime(default= lambda d: datetime.datetime.now())







Comentarios

Entradas populares de este blog

20. Desarrollando con Odoo (15). Permisos y grupos. Crear usuarios de la aplicación. Restringir permisos a usuarios

2. El Modo desarrollador

10. Crear clave de la API