Comprendre le mécanisme d'héritage dans le développement d'applications odoo

Introduction

Le concept d'héritage, est l'un des très puissants  concepts  qui permet d'une part  la réutilisation et l'amélioration du code et des modules existants,  et d'autre part de garder toujours une copie du  code d'origine . 

Le Concept d'héritage ,un concept du paradigme Orienté Objet, est largement utilisé par odoo, sous plusieurs formes , l'extension des classes python, l'héritage des Vues et interfaces, et l'héritages des pages webs, ect.

Types D'héritage Odoo 

 On distingues quatre type d'héritage, classique, délégation, mixin et prototype, on verra par la suite des cas concrets :

L'héritage Classique : 

C'est le type le plus utilisé par odoo, son mode de fonctionnement consiste, à étendre une classe,  une vue ou autre composant, on ajoutant/ modifiant  d'autres champs de base de donnée, des méthodes python,  des Vues.

L'héritage par  Délégation 

C'est un héritage qui consiste, à ce que l'instance de  la classe créée va contenir l'instance de la classe origine , à chaque fois ou un objet est créé sur la nouvelle classe, un autre objet sera créé sans copier les donnée, c'est un lien de type manyto-one  (plusieurs à un ) qui sera créé au lieu de la duplication de la classe mère . 

L'héritage multiple (mixin)

On utilisant ce type d'héritage, la nouvelle classe va hériter de plusieurs classes mères , le nom de ce type d’héritage est l’héritage mixin (mixed in)

L'héritage  prototype 

L'héritage protocole, permet de copier totalement  Les données de la classe mère, ce type est généralement utilisé pour des classes abstraites, pour éviter a duplication des données.

L'image suivante tiré du site officiel d'odoo,  illustre les types d’héritage .

Etude de cas : l'exemple gestion de location des salles :

Pour bien comprendre le mécanisme d'héritage, nous allons créer un module, qui va étendre les fonctionnalités de notre application agence de location des salles et des bureau, déjà créé  (Consulter  l'article votre première application odoo, et l'article :  les interfaces odoo de  bases . ) 

Création du module qui va étende notre application gestion de location  des bureaux et salles : 

Au lieu, de réécrire tout le code que nous avons déjà créer dans l'application agence, nous allons créer un module qui va dépendre ce cette application, et nous lui apportons les modifications nécessaires , avec la création d'autres fonctionnalités spécifique à notre nouveau module .
          Comme dans la première partie, nous allons créer un module , qui portera le nom location_clt, avec les éléments de base qui sont le fichier, __manifest__.py  et __init__.py , et préparer es deux répertoires /models et views. 
Mettez dans le fichier __init__.py  l'instruction  suivante : 

from . import models.

Mettez dans le fichier __manfist__ les instruction suivante  .

'name': ' Gestion de Location bureau et de salles- Ajout des clients ', 

'description': ' Gérer la location des clients ', 

'author': 'BENHAMIDA Mustapha', 

'depends': ['location_app'], 

'application': False,

 }

Dans la partie 'depends ' , nous avons fait appel à notre module créé, odoo va donc rechercher dans le répertoire courant le nom du module, pour utiliser les fonctionnalités de ce module, les modifier, ou ajouter d'autres spécifique à notre nouveau module .
Un module peut dépendre donc de plusieurs modules, dans ce cas vous les mentionnez après le mot depends , séparés par des virgules. Si ces modules ne sont pas installé , ils seront automatiquement installés lorsqu'on en installe le module qui les utilise.
Remarquer que dans l'option 'application ' , nous avons mis False, pour spécifier qu'il 'agit d'u n module qui va étendre une autre application (location_app).

Héritage classique : Modification sur la classe de l'application de base : Ajouter un nouveau champ/ Modifier un champ est une fonction

Pour comprendre le mécanisme de l'héritage, nous allons ajouter un champ pour la modèle salle, puis on va afficher ce nouveau champs dans la vue formulaire, et vue Liste.
Nous ajoutons à notre classe Salle, un champ pour préciser la surface de cette salle , et modifier le champ description, en le rendant un champ HTML .

Nous modifiant , en utilisant l'héritage, par la suite la fonction qui permet de déterminer le type de salle , en affinant plus la recherche sur le nombre de places (inférieur ou égale à 20 places, c'est une salle de réunion, entre 20 et 100 personnes c'est une salle de formation, et on garde la condition , pour plus de 100 personnes , une salle de conférence .)
Dans la dossier , models, créer un fichier models/agence_clt .py qui va contenir notre code du module location_clt. ajouter l'instruction import au fichier __intit__.py:


from odoo import  fields,models
from  odoo.exceptions import  Warning

class Salle (models.Model):
    _inherit = 'agence.salle'

    def check_typesalle(self):
        for salle in self:
            if not salle.nbplaces:
                raise Warning(' Ajouter le nombre de places pour  %s' % salle.name)
            if salle.nbplaces <= 20:
                raise Warning(' C est une salle de réunion !  ')
            elif  salle.nbplaces >10 and  salle.nbplaces < 100 :
                raise Warning(' C est une salle de formation ! ')
            else:
                raise Warning(' C est une salle de Conférence !  ')
        return True

    # On ajoute le champ surface
    surface=fields.Integer(' Surface ' )
    # On modifie le champ description, en le rendant html
    description = fields.Html('desciption détaillé')

En utilisant le mot clé _inherit suivi par le modèle dont va hériter,le compilateur d'odoo reconnait automatiquement tous les champs du modèle origine, et ajouter les champs qu'on déclare dans la classe qui étends la classe mère (ici nous avons ajouté le champ surface au modèle salle).

Les méthodes suivent le même principe, dans cet exemple nous avons redéfinit la fonction chek_typesalle . 


Étendre les Vues Formulaire et liste pour afficher notre nouveau champ :

pour afficher le/les nouveaux champs dans la vue formulaire , et liste ou n'importe quel autre vue , on doit modifier la vue mère de notre application agence , pour se faire on créer un fichier xml, qui sera nommé agence_clt.xls, dans la dossier /views et on va y mettre les instructions qui permettent d'hériter la vue mère, on l'ajoutant / modifiant les nouveau champs .


Modifier la vue Formulaire et Liste : pour afficher le champ surface dans la vue formulaire et liste , on doit ajouter un enregistrement (record) qui va décrire, la vue sur laquelle on doit hériter , à travers l'identifiant de la vue sur le formulaire de base, puis on précise le champs à prendre comme repère, pour ajouter avant ce champ , ou après .

Dans le fichier, xml /views/agence_clt;   Mettez le code suivant :


<?xml version="1.0"?>
<odoo>
    <record id="view_form_salle_extend" model="ir.ui.view">
        <field name="name"> Salle : Ajouter Suface </field>
        <field name="model">agence.salle</field>
        <field name="inherit_id" ref="location_app.view_form_salle"/>
        <field name="arch" type="xml">
            <field name="isactif" position="after">
            <field name="surface" />
            </field>
        </field>
    </record>

    <record id="view_tree_salle_extend" model="ir.ui.view">
        <field name="name"> Salle : Ajouter Suface </field>
        <field name="model">agence.salle</field>
        <field name="inherit_id" ref="location_app.view_tree_salle"/>
        <field name="arch" type="xml">
            <field name="isactif" position="before">
            <field name="surface" />
            </field>
        </field>
    </record>
</odoo>

Vous remarquez que le principe est le même pour définir la vue formulaire et liste, la seule différence réside dans l’utilisation du champ "inherit_id" qui permet d’identifier la vue dont on vas faire l'héritage.

pour l'attribut "position": on précise la position où on doit insérer notre nouveau champ , dans cet exemple , on a utilisé après "after" dans la vue formulaire, et "avant ("before"), dans la vue liste . d'autres attributs peuvent être utilisés comme "inside" à l'intérieur, ou replace pour remplacer .

Utilisation d' Xpath pour étendre une vue à partir d'un point : Dans le cas où, nous n'avons pas les noms des champs , comme par exemple groupe, notebook , page, .... on utilise l'expression xpath d'XML.

Exemple d L'expression xpath :


<  expr="//field@name='isactif'] "  position="after">
  <field name="surface" />   </xpath>


Pour le champ que nous avons modifié (Description), le compilateur prends en considération le nouveaux  format (html) sans nécessité de le  déclarer dans la vue xml.

Pour visualiser les changements, on doit redémarrer le serveur , et  mettre à jour notre module . Allez donc dans la partie application, enlever le filtre par défaut et taper location , et installer le module location_clt.

Voila le résultat qu'on aura après l'installation du module 

Héritage prototype :

Lorsqu'on utilise l'héritage de prototype, toute les composants de la classe mère seront dupliqué dans la classe fille.

Lorsqu'on a étendue la classe Salle, nous avons utilisé le mot clé _inherit , mais nous n'avons pas donner un nom à notre classe, le nom implicite est agence.salle.

Par contre, si on précise le nom de la nouvelle classe en mettant _name=nouvelle_classe, on utilise l’héritage prototype qui permet de dupliquer tous les données de la classe mère.

En pratique ce type d'héritage, est utilisé avec les classes abstraites mixin, vue qui' elles n'ont pas des données à sauvegarder sur la base de données, mais plutôt des méthodes à réutilisés suivant le contexte .

L'héritage par a délégation

Contrairement à l'héritage prototype, l'héritage par délégation, nous évite à dupliquer les données , en utilisant le principe de composition du modèle.

Ce type est très pratique, et il est utilisé généralement avec les modèles de base d'odoo, et repose sur le principe de composition (composant/composés).

A chaque fois qu'on crée une instance de la nouvelle classe , on crée une instance dans la classe mère .

Pour être utilisé, l'héritage délégation, en ajoutant le mot clé _inherits (avec le s) au lieu de _inherit , on va utiliser ce type , avec un cas pratique pour bien comprendre le principe .

Ajouter le modèle locataire en utilisant l'héritage par délégation : Pour pouvoir représenter les clients , on va ajouter dans notre application le modèle locataire , pour enregistrer la base de données des locataires .

Odoo possède par défaut, un model qui comporte plusieurs informations à introduire dans un contact, client ou fournisseur ( nom, email, photo,adresse, sit web ...). Ce modèle est nommé res.partner .

Pour éviter de recréer tous ces informations , on va les réutiliser en utilisant le principe d'héritage par délégation, et on ajoute le champ code locataire pour fidéliser les clients ( à travers les points qui accumulent à chaque location).

Héritage par délégation en pratique :

Ajout du nouveau modèle locataire avec les droits d'accès et les vues
Ajout du modèle locataire : Pour plus d'organisation, on ajoute le nouveau fichier contenant le nouveau modèle , dans le répertoire models et on le nomme locatire.py ( /models/locataire.py) , et on le mentionne dans le fichier__init__.py de cette manière :

from . import  agence_clt 

from . import  locataire

 Mettez dans le fichier locataire.py la partie du code suivante : 

from odoo import fields, models
class Locataire(models.Model):
    _name = 'agence.locataire'
    _description = 'Locataire d agence'
 
    code_locataire = fields.Char()
    partner_id = fields.Many2one(
     'res.partner',
      delegate=True,
      ondelete='cascade',
      required=True)


Ici, nous avons activé l'héritage par délégation, on utilisant a relation manytoone (plusieur à un), et en mettant l'attribut delegate=True .                         L'attribut ondelete='cascade', veut dire que si on supprime un objet de la nouvelle classe, l'objet relié de la classe d'origine sera supprimé. 


Ajout des droits d'accès   :   Pour visualiser les nouveaux  modèles, on doit leur définir les droits d'accès , on va donc créer le répertoire /security/ et à l'intérieur on créer un fichier csv,  nommé ir.model.access.csv . dedans  on va préciser les droits d'accès ( Pour plus de détailles consulter l'article créer votre première application odoo) . 

Mettez le code suivant dans le fichier csv . 


id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_locataire_user,LocataireUser,model_agence_locataire,location_app.agence_group_user,1,0,0,0
access_locataire_manager,LocataireManager,model_agence_locataire,location_app.agence_group_manager,1,1,1,1

Ajout l'option locataire au menu agence :

Par la suite on ajoute l'option du menu locataire qui permet l'accès à liste des locataire, on doit l'ajouter à notre menu d'agence que nous avant déjà créé dans notre application agence _app

Pour se faire on doit créer le répertoire /views/ et à l'intérieur on créer un fichier xml nommé /views/ locataire_menu.xml .

Mettez le code suivant dans le fichier xml.


<?xml version="1.0"?>
<odoo>
        <act_window id="action_agence_locataire"
        name="Les Locataires"
        res_model="agence.locataire"
        view_mode="tree,form" />
        <menuitem id="menu_agence_locataire"
        name="Locataires "
        action="action_agence_locataire"
        parent="location_app.menu_agence" />
</odoo>


Créer les Vues Form et Liste pour le nouveau modèle locataire :

Pour compléter notre module, on ajouter la vue formulaire et liste , pour qu'on puisse introduire et visualiser les données des locataires.

Toujours dans le répertoire /views/ on crée un fichier xml nommé /views/locataire .xml , à l’intérieur , Mettez le code suivant dans le fichier csv .


<?xml version="1.0"?>
<odoo>
    <record id="view_form_locataire" model="ir.ui.view">
        <field name="name">Les locataire (reste en premier) </field>
        <field name="model">agence.locataire</field>
        <field name="arch" type="xml">
            <form>
            <group>
                <field name="name" />
                <field name="email" />
                <field name="website" />
                <field name="street" />
                <field name="code_locataire" />
            </group>                
            </form>
        </field>
    </record>
    <record id="view_tree_member" model="ir.ui.view">
        <field name="name">gestion des locataires </field>
        <field name="model">agence.locataire</field>
        <field name="arch" type="xml">
        <tree>
            <field name="name" />
            <field name="code_locataire" />
        </tree>
</field>
</record>
</odoo>

Puisque  nous avons hérité du fichier res.partner , nous avons ajouté dans notre formulaire , trois champs , même si nous les avons pas déclarés dans la classe locataire, la seul attribut spécifique au nouveau modèle, c'est le code locataire . 

A Travers cet exemple, nous pouvons comprendre l'intérêt de l’utilisation de l'héritage qui nous permet de gagner du temps et d'efficacité .
 
redémarrage et mise à jour du serveur      :  

 Pour visualiser les changements , on doit redémarrer le serveur et mettre à jour notre module. 

Vous pouvez remarquer l'ajout de la nouvelle option du menu qui permet d'afficher les informations des locataires .

Étendre le modèle locataire en utilisant l’Héritage mixin

On utilise principalement l’héritage prototype , lorsqu'on utilise la classe mixin (mixed in) qui permet d'étendre des classe abstraites issue du model abstratc.Models , qui ne contiennent pas des données, par contre elle contiennes des composants qu'on peut les réutiliser à d'autres modèles selon nos besoins.

Odoo propose plusieurs addons (modules) qui définissent des mixins , on trouve principalement deux mixins qui sont les plus utilisés sont :

- mail.thread : qui permet de suivre les flux des emails , des notifications et des message discussions

- mail.activity.mixin: qui permet de suivre les activités et les taches à programmer .

Ces deux mixin, utilisent le mdule mail, nous devons donc le déclarer dans le fichier __manifest__.py de cette façon : 

'depends': ['location_app','mail'],

Pour illustrer l'utilisation de l'héritage mixin, dans la classe locataire , il suffit d'ajouter  la ligne suivante : 

_inherit = ['mail.thread', 'mail.activity.mixin']

Le code complet de la classe locataire.py sera donc : 

from odoo import fields, models
class Locataire(models.Model):
    _name = 'agence.locataire'
    _description = 'Locataire d agence'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    code_locataire = fields.Char()
    partner_id = fields.Many2one(
        'res.partner',
        delegate=True,
        ondelete='cascade',
        required=True)

Par la suite on ajouter dans fichier /views/locataire.xml , dans la section formulaire ,  les instructions suivantes : 

<!-- Les champs de mail mixin -->
<div class="oe_chatter">
  <field name="message_follower_ids" widget="mail_followers"/>
  <field name="activity_ids" widget="mail_activity"/>
  <field name="message_ids" widget="mail_thread"/>
</div>

 Le code complet du fichier locataire.xml sera le suivant : 


<?xml version="1.0"?>
<odoo>
    <record id="view_form_locataire" model="ir.ui.view">
        <field name="name">Les locataire (reste en premier) </field>
        <field name="model">agence.locataire</field>
        <field name="arch" type="xml">
            <form>
            <group>
                <field name="name" />
                <field name="email" />
                <field name="website" />
                <field name="street" />
                <field name="code_locataire" />
            </group>
                <!-- Les champs de mail mixin -->
                <div class="oe_chatter">
                  <field name="message_follower_ids" widget="mail_followers"/>
                  <field name="activity_ids" widget="mail_activity"/>
                  <field name="message_ids" widget="mail_thread"/>
                </div>
            </form>
        </field>
    </record>
    <record id="view_tree_member" model="ir.ui.view">
        <field name="name">gestion des locataires </field>
        <field name="model">agence.locataire</field>
        <field name="arch" type="xml">
        <tree>
            <field name="name" />
            <field name="code_locataire" />
        </tree>
</field>
</record>
</odoo>

Après redémarrage du serveur,  mise  à jour de notre module , on aura comme résultat la vue formulaire du locataire sous la forme suivante : 

Autres Types d'héritage

Dans odoo , D'autres composants peuvent être étendue, comme les controlleurs web, les groupes de sécurité , les méthode python , les Templates qweb, etc. En bref ça touches les trois couche du Modèle MVC , la couche Modèle, La couche Vue et la couche Controller.

Conclusion

Nous avons vue comment odoo utilise le concept d'héritage, comme étant un outil puissant qui permet l’amélioration et le développement rapide des applications basées sur le framework odoo

Nous avons vu aussi , les type d'héritage qui sont l’héritage classique, prototype délégation et mixin , tout cela avec des exemples pratiques qui permettent de mieux comprendre le mécanisme d'héritage sous odoo

Télécharger gratuitement votre  guide odoo ! 

Télécharger Gratuitement votre guide Odoo

Découvrez comment , Odoo  est une vraie opportunité pour les entreprises et les développeurs !


Télécharger !