Définition des relations entre les modèles Odoo
Introduction
Dans l’article précédent, nous avons fait un tour sur les champs non relationnels.
Il existe d’autre champs qui permettent de définir les relations entre les modèles.
Dans cet article nous allons présenter, avec des cas pratiques, les trois types de relations qui existent entre les modèles, toujours en se basant sur notre exemple de l’application gestion d’une agence de location de véhicules.
Types de relations entre les modèles :
Il existe trois types de relations entre les modèles :
- Plusieurs à un (m2o abréviation de many-to-one)
- Un à Plusieurs (o2m abréviation de one -to-many)
- Plusieurs à Plusieurs (m2m abréviation de many-to-many)
Si on revient à notre exemple du modèle ‘agence.vehicule’, chaque véhicule appartient à une marque (Renalut, Ford, …), c’est une relation ManyToOne.
A l’autre côté, Une marque donnée peut contenir plusieurs Véhicules, c’est une relation OneToMany qui est l’inverse de la relation ManyToOne.
Dans la définition de notre modèle, nous avons fait appel à la relation ManyToMany, du fait que chaque véhicule peut être conduit par plusieurs conducteurs, et que chaque conducteur peut conduire plusieurs véhicules.
Remarque importante :
Les règles entre les relations, sont généralement définit par l’utilisateur final, on peut donc trouver pour le même exemple cité précédemment d’autres contraintes, par exemple une société peut exiger que chaque véhicule ne puisse être conduit que par un seul conducteur, dans ce cas nous faisons appel à la relation ManyToOne au lieu de la relation ManyToMany expliqué précédemment.
Relation Plusieurs à un (m2o abréviation de many-to-one)
Dans notre modèle exemple ‘agence.vehicule’, nous avons présenté l’information marque, par le champ marque, et pour définir ce champ, il fallait le saisir à chaque fois, même si il se répète, ce qui n’est pas pratique.
La meilleur solution est de créer un autre modèle à part qui sera nommé ‘agence.marque’, et dans le modèle ‘agence.vehicule’ on définit le champ marque_id, qui fait appel au modèle’agence.marque’. Cette relation est la relation ManyToOne qui se traduit par : à chaque véhicule, on associe une marque parmi plusieurs.
Pour déclarer ce type de relation, on utilise la syntaxe suivante :
marque_id=fields.Many2one(
'agence.marque',
# optional:
ondelete='set null',
context={},
domain=[],
string='Marque')
Le paramètre ‘ondelete’ peut avoir trois valeurs : (‘set null’ , ‘cascade’ ou ‘restrict’) , et qui représente qu’est ce qui se passe lorsque l’enregistrement relié est supprimé :
La valeur ‘set null’ veut dire mettre cet enregistrement la valeur ‘null’, ‘cascade’ veut dire la suppression de l’enregistrement relié entraine la suppression de cet réenregistrement et ‘restrict’ empêche la suppression de l’enregistrement en cours.
On met dans l’attribut context, généralement les valeurs par défaut, alors que dans le paramètre domain, on active un filtre pour limiter l’affichage des enregistrements reliés.
Relation Un à Plusieurs (o2m abréviation de one -to-many)
La relation OnetoMany est la relation inverse, de la relation ManyToOne. Dans l’exemple précédent, nous avons utilisé la relation ManyToOne, qui représente que chaque véhicule appartient à une marque parmi plusieurs.
A l’inverse, Chaque Marque Véhicule peut concerner plusieurs Véhicules.
La représentation de cette relation se fait de la manière suivante :
vehicules_ids=fields.One2many(
'agence.vehicule',
'marque_id',
string='Véhicules de cette marque'
)
Nous avons précisé le modèle en relation avec ce modèle (agence.marque) qui est ‘agence.vehicule’ ainsi que la clé qui va migrer vers le modèle lié (marque_id)
Relation Plusieurs à Plusieurs (m2m abréviation de many-to-many)
Nous avons utilisé cette relation dans notre exemple, dans la déclaration de la liste des conducteurs d’un véhicule qui se traduit par la relation chaque Véhicule peut avoir plusieurs conducteurs.
Inversement, chaque conducteur peut conduit plusieurs véhicules, nous avons donc en total une relation ManyToMany.
Nous avons déjà défini la relation côté agence .vehicule, nous ajoutons donc la relation à côté des conducteurs de cette façon (pour simplification nous avons hérité du modèle de base res.partner ).
vehicules_autorises_ids= fields.Many2many(
'agence.vehicule',
string='Véhicules autorisés à être conduits',
# relation='agence_vehicule_res_partner_rel'
)
Cette relation déclenche la création d’une table intermédiaire, dans la base de données qui aura deux colonnes l’un porte l’ID de la table 1 et la deuxième sera l’ID de la table 2.
Odoo traite automatique ce type de relation, et met le nom de la table créée par défaut en concaténant le nom de la table1 et le nom de la table 2.
Dans l’attribut, relation on peut modifier ce comportement par défaut en personnalisant le nom de la table qui va relier les deux modèles.
Cela est pratique est peut être parfois indispensable lorsque la taille de la concaténation des noms des deux tables dépasse les 63 permis par le SGBD PostgreSQL .
Récapitulatif et application sur Notre exemple Agence de Location de Véhicules :
Afin d’illustrer les concepts précédents expliqué, nous revenons vers notre exemple de base (vous pouvez consulter l’article précédent pour plus de détails) pour définir les nouveaux modèles et leurs relations, en suivant les étapes suivantes :
1- Remplacement du champ marque par marqu_id :
Allez dans le fichier python agence_vehicule.py, puis effectuez les mises à jours suivantes :
o Remplacer le champ marque par les lignes suivantes :
marque_id=fields.Many2one(
'agence.marque',
# optional:
ondelete='set null',
context={},
domain=[],
string='Marque')
o Supprimer le champ marque de l’attribut _order
_order = 'date_achat desc'
En effet, l’attribut _order n’accepte pas le champ marqu_id comme champ de trie.
o Redéfinissez la fonction _name_get par le code suivant (remplacez la variable marque par marque_id.name) :
def name_get(self):
result = []
for record in self:
rec_name = "%s -%s -%s" % (record.marque_id.name,record.modele,record.matricule)
result.append((record.id, rec_name))
return result
2- Ajouter un nouveau fichier python, qui porte le nom agence_marque.py, dans le répertoire models et mettez dedans el code suivant :
from odoo import models, fields
class AgenceMarque(models.Model):
_name='agence.marque'
_description = 'Les Marques des Véhicules'
# On met le champ name
name=fields.Char('Nom de la Marque')
logo=fields.Binary('Logo')
vehicules_ids=fields.One2many(
'agence.vehicule',
'marque_id',
string='Véhicules de cette marque'
)
3- Ajouter un nouveau fichier python, qui porte le nom agence_conducteur.py dans le répertoire models et mettez dedans el code suivant :
from odoo import models, fields
class ResPartner(models.Model):
_inherit = 'res.partner'
vehicules_autorises_ids= fields.Many2many(
'agence.vehicule',
string='Véhicules autorisés à être conduits',
# relation='library_book_res_partner_rel'
)
4- Mettez à jour le fichier __init__.py qui se trouve dans le répertoire models et en ajoutant les deux nouveaux fichiers python :
from . import agence_vehicule
from . import agence_marque
from . import agence_conducteur
5- Pour afficher les changements et les nouveaux modèles créé, nous devons créé les vues correspondantes avec la définition des droits d’accès pour les deux nouveau modèles agence.marque et agence .conducteur , pour se faire on suit les étapes suivantes :
6- Dans le répertoire views, créez le fichier xml ‘agence_marque.xml’ qui va définir les vues pour le modèle ‘agence.marque’ et mettez dedans le code suivant :
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id='agence_marque_action' model='ir.actions.act_window'>
<field name="name"> Gestion des Marques </field>
<field name="res_model">agence.marque</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem name="Marques" id="agence_marque_menu"
parent="agence_base_menu" action="agence_marque_action"/>
<record id="agence_marque_view_form" model="ir.ui.view">
<field name="name">Agence Marque Form</field>
<field name="model">agence.marque</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="name"/>
<field name="logo" widget="image" class="oe_avatar"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="agence_marque_tree" model="ir.ui.view">
<field name="name">Agence Marque Lise</field>
<field name="model">agence.marque</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="logo"/>
</tree>
</field>
</record>
<record id="agence_marque_search" model="ir.ui.view">
<field name="name">Agence Marque Recherche</field>
<field name="model">agence.marque</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
</search>
</field>
</record>
</odoo>
Dans ce fichier, nous avons ajouté le menu qui permet de gérer les marques, ainsi que les vues nécessaires (Formulaire, Recherche et Liste)
7- Dans le répertoire views, créez le fichier xml ‘agence_conducteur.xml’ qui va définir les vues pour le modèle ‘agence.conducteur’ et mettez dedans le code suivant :
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id='agence_cond_action' model='ir.actions.act_window'>
<field name="name"> Gestion des Conducteurs </field>
<field name="res_model">res.partner</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem name="Conducteurs" id="agence_cond_menu"
parent="agence_base_menu" action="agence_cond_action"/>
<record id="agence_cond_view_form" model="ir.ui.view">
<field name="name">Agence Cond Form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<field name="type" position="before">
<field name="vehicules_autorises_ids" widget="many2many_tags" />
</field>
</field>
</record>
</odoo>
Dans ce fichier, nous avons ajouté le menu qui permet de gérer les conducteurs.
Nous avons aussi personnalisé la vue Formulaire, en l’héritant de la vue formulaire Odoo de base qui permet de représenter la vue formulaire du modèle res.parnter, en l’ajoutant uniquement le champ qui affiche les véhicules associés à ce conducteur sous forme de widget many2many_tags.
Nous laissons les vues liste et recherche s’afficher par défaut par l’ORM Odoo.
8- Dans le répertoire security mettez à jour le fichier groups.xml en l’ajoutant les groupes des deux nouveaux modèles :
<record id="group_ag_marque_managers" model="res.groups">
<field name="name">Agence Marque Managers</field>
<field name="users" eval="[(4, ref('base.user_admin'))]"/>
</record>
<record id="group_ag_cond_managers" model="res.groups">
<field name="name">Agence Cond Managers</field>
<field name="users" eval="[(4, ref('base.user_admin'))]"/>
</record>
9- Toujours dans le répertoire security, mettez à jour le fichier ir.model.access.csv en l’ajoutant les droits d’accès des deux nouveaux modèles :
acl_agence_m_users,agence.marque default,model_agence_marque,,1,0,0,0
acl_agence_marque_admin,agence.marque_admin,model_agence_marque,group_ag_marque_managers,1,1,1,1
acl_agence_c_users,res.partner default,model_res_partner,,1,0,0,0
acl_agence_cond_admin,res.partner_admin,model_res_partner,group_ag_cond_managers,1,1,1,1
10- Mettez à jour le fichier __manifest__.py, en déclarant les nouvelles vues :
'views/agence_marque.xml',
'views/agence_conducteur.xml'
En fin, redémarrer le service Odoo, puis mettez à jour votre application, C’est tout se passe bien vous aurez comme résultat, ce qui ressemble à la page suivante :
Conclusion
Nous avons étudié dans cet article, les trois types de relation qui existent entre les modèles, ManyToOne (Plusieurs à Un) , OneToMany (Un à Plusieurs) et ManyToMany (Plusieurs à Plusieurs) .
Nous avons vu également comment présenter ces relations avec un exemple concret qu’on peut prendre comme modèle pour présenter plusieurs autres cas pratiques.
Télécharger gratuitement votre guide odoo !
Découvrez comment , Odoo est une vraie opportunité pour les entreprises et les développeurs !