Chapitre 6 : Création d’un module sur Odoo (part 12)

Champs de calcul

Les relations entre les modèles sont un élément clé de tout module Odoo. Elles sont nécessaires à la modélisation de toute affaire. Cependant, nous pouvons vouloir des liens entre les champs d’un modèle donné. Parfois, la valeur d’un champ est déterminée à partir des valeurs d’autres champs, nous voulons aider l’utilisateur à saisir des données.

Ces cas sont pris en charge par les concepts de champs calculés et de modifications (onchanges). Bien que ce chapitre ne soit pas techniquement complexe, la sémantique de ces deux concepts est très importante. C’est également la première fois que nous écrivons de la logique Python. Jusqu’à présent, nous n’avons rien écrit d’autre que des définitions de classes et des déclarations de champs.

Champs calculés

Objectif : à la fin de cette section :

Dans le modèle immobilier, la surface totale et la meilleure offre doivent être calculées :

Compute fields

  • Dans le modèle d’offre de biens immobiliers, la date de validité doit être calculée et peut être mise à jour :

Compute field with inverse

Dans un module immobilier, nous avons défini la surface habitable ainsi que la surface du jardin. Il est donc naturel de définir la surface totale comme la somme des deux champs. Pour ce faire, nous utiliserons le concept de champ calculé, c’est-à-dire que la valeur d’un champ donné sera calculée à partir de la valeur d’autres champs.

Jusqu’à présent, les champs ont été stockés directement dans la base de données et extraits directement de celle-ci. Les champs peuvent également être calculés. Dans ce cas, la valeur du champ n’est pas extraite de la base de données mais calculée à la volée en appelant une méthode du modèle.

Pour créer un champ calculé, créez un champ et lui affectez un attribut compute le nom d’une méthode. La méthode de calcul doit définir la valeur du champ calculé pour chaque enregistrement de self.

Par convention, les méthodes de calcul sont privées, ce qui signifie qu’elles ne peuvent pas être appelées depuis le niveau de présentation, mais uniquement depuis le niveau métier (voir le chapitre 1 : Présentation de l’architecture). Les méthodes privées ont un nom commençant par un trait de soulignement _.

Dépendances

La valeur d’un champ calculé dépend généralement des valeurs d’autres champs de l’enregistrement calculé. L’ORM attend du développeur qu’il spécifie ces dépendances sur la méthode de calcul avec le décorateur depends(). Les dépendances données sont utilisées par l’ORM pour déclencher le recalcul du champ lorsque certaines de ses dépendances ont été modifiées :

from odoo import api, fields, models

class TestComputed(models.Model):
    _name = "test.computed"

    total = fields.Float(compute="_compute_total")
    amount = fields.Float()

    @api.depends("amount")
    def _compute_total(self):
        for record in self:
            record.total = 2.0 * record.amount

Remarque

self est une collection.

L’objet self est un jeu d’enregistrements, c’est-à-dire une collection ordonnée d’enregistrements. Il supporte les opérations standard de Python sur les collections, par exemple len(self) et iter(self), ainsi que des opérations supplémentaires sur les ensembles telles que recs1 | recs2.

L’itération sur self permet d’obtenir les enregistrements un par un, chaque enregistrement étant lui-même une collection de taille 1. Vous pouvez accéder/assigner des champs sur des enregistrements individuels en utilisant la notation point, par exemple record.name.

Activité : créer un champ calculé

Dans cet exercice, nous allons créer un champ calculé appelé « capitalized_name » dans le modèle HospitalPatient du fichier patient.py. Ce champ utilisera une fonction de calcul _compute_capitalized_name pour convertir le nom en minuscules en majuscules. Voici les étapes à suivre :

  1. Ouvrez le fichier patient.py qui définit le modèle HospitalPatient.
  2. Ajoutez l’importation du module fields dans la liste des importations en haut du fichier :
from odoo import models, fields
  1. Dans la classe HospitalPatient, ajoutez le champ calculé « capitalized_name » et la fonction de calcul _compute_capitalized_name :
class HospitalPatient(models.Model):
    _name = 'hospital.patient'
    _description = "Patient Records"

    name = fields.Char(string='Name', required=True)
    capitalized_name = fields.Char(string='Capitalized Name', compute='_compute_capitalized_name')

    @api.depends('name')
    def _compute_capitalized_name(self):
        for rec in self:
            if self.name:
                self.capitalized_name = patient.name.upper()
            else:
                self.capitalized_name = ''
  1. Enregistrez vos modifications dans le fichier patient.py.
  2. Redémarrez votre serveur Odoo pour appliquer les modifications.

Maintenant, lorsque vous créez ou modifiez un patient et saisissez un nom, le champ calculé « capitalized_name » sera automatiquement mis à jour à l’aide de la fonction _compute_capitalized_name. Cette fonction convertit le nom en minuscules en majuscules à l’aide de la méthode upper() de Python.

Cela permet d’afficher automatiquement le nom en majuscules dans le champ « Capitalized Name » du formulaire du patient. Si aucun nom n’est saisi, le champ sera vide.

Cet exercice démontre l’utilisation des champs calculés et des fonctions de calcul pour effectuer des opérations sur les données du modèle et les afficher de manière transformée dans l’interface utilisateur.

Rendre la fonction capable de gérer plusieurs enregistrements

La boucle for rec in self: est utilisée dans Odoo pour itérer sur un ensemble d’enregistrements d’un modèle. Dans le contexte d’une méthode de modèle, self fait référence à l’ensemble des enregistrements actuellement traités.

Lorsque vous utilisez for rec in self:, vous parcourez chaque enregistrement individuellement et pouvez effectuer des opérations sur chacun d’entre eux. L’objet rec représente un enregistrement spécifique dans l’ensemble.

Voici un exemple d’utilisation de la boucle for rec in self: :

class HospitalPatient(models.Model):
    _name = 'hospital.patient'
    _description = "Patient Records"

    name = fields.Char(string='Name', required=True)
    age = fields.Integer(string='Age')

    def print_patient_details(self):
        for rec in self:
            print(f"Patient Name: {rec.name}")
            print(f"Patient Age: {rec.age}")

Dans cet exemple, la méthode print_patient_details est définie dans le modèle HospitalPatient. Lorsqu’elle est appelée, la boucle for rec in self: itère sur chaque enregistrement de self, c’est-à-dire tous les patients. À chaque itération, les détails du patient sont affichés à l’aide de rec.name et rec.age.

La boucle for rec in self: est un moyen pratique de travailler avec plusieurs enregistrements dans Odoo. Vous pouvez effectuer des opérations spécifiques sur chaque enregistrement individuel en utilisant l’objet rec. Cela permet de traiter les données de manière itérative et d’effectuer des opérations en fonction des caractéristiques de chaque enregistrement.

Rendre le champ de calcul stocké

Activité : Rendre le champ de calcul stocké

Pour rendre le champ de calcul stocké, vous pouvez utiliser un champ persistant tel que fields.Char ou fields.Text. Voici comment modifier l’exercice précédent pour rendre le champ « capitalized_name » stocké :

  1. Ouvrez à nouveau le fichier patient.py qui définit le modèle HospitalPatient.
  2. Modifiez la définition du champ « capitalized_name » en utilisant un champ persistant :
capitalized_name = fields.Char(string='Capitalized Name', compute='_compute_capitalized_name', store=True)
  1. Enregistrez vos modifications dans le fichier patient.py.

Maintenant, lorsque vous créez ou modifiez un patient et saisissez un nom, le champ « capitalized_name » sera automatiquement mis à jour et stocké dans la base de données. Cela signifie que la valeur calculée sera conservée même après la fermeture et la réouverture de l’application.

Il est important de noter que l’utilisation de store=True peut augmenter la taille de la base de données car les valeurs calculées sont stockées. Assurez-vous de prendre en compte les performances et l’espace de stockage nécessaires avant de décider de rendre un champ calculé stocké.