Introduction
Si, en ouvrant une commande client dans Odoo, vous avez déjà ajouté, modifié ou supprimé des lignes directement depuis la fiche sans changer d’écran, vous avez utilisé un One2many. C’est un mécanisme central du modèle de données Odoo : il permet d’agréger plusieurs enregistrements enfants autour d’un seul enregistrement parent, et comprend à la fois l’ergonomie et la logique relationnelle derrière de nombreuses vues de l’ERP.
Ce guide présente l’essentiel à connaître : ce qu’est concrètement un One2many, comment l’ORM d’Odoo le matérialise, dans quels scénarios l’utiliser, et comment le mettre en place soit via Odoo Studio sans code, soit en le définissant directement dans un module Python.
Que vous soyez utilisateur métier curieux de la structure de vos données ou développeur en quête d’un tutoriel pratique sur les champs Odoo, ce texte synthétise les points utiles pour maîtriser le One2many.
Qu’est-ce que le champ One2many dans Odoo
Un One2many est un type de champ relationnel dans Odoo qui représente une relation parent → plusieurs enfants. Il donne l’illusion, dans le modèle parent, d’une liste d’éléments liés provenant d’un autre modèle.
Concrètement : une facture peut contenir plusieurs lignes, une commande plusieurs positions, un projet plusieurs tâches. Le parent ne stocke pas physiquement ces lignes ; il présente simplement l’ensemble des enregistrements enfants qui lui sont associés.
Dans l’interface, le One2many se manifeste le plus souvent comme une liste intégrée dans une vue formulaire. Cette table embarquée permet de créer, éditer ou supprimer des éléments enfants sans quitter la fiche parent — un usage très courant dans les modules commerciaux, comptables ou projets.
Différence avec les autres champs relationnels
L’ORM d’Odoo propose trois relations majeures :
- Many2one : un enregistrement fait référence à un unique enregistrement d’un autre modèle (ex. : une commande vers un client).
- One2many : un enregistrement regroupe plusieurs enregistrements d’un autre modèle (ex. : une commande vers ses lignes).
- Many2many : relation multiple réciproque où chaque côté peut référencer plusieurs enregistrements de l’autre (ex. : produits et étiquettes).
Le One2many est approprié quand chaque enregistrement enfant n’appartient qu’à un seul parent. Si un enfant doit être partagé entre plusieurs parents, il faut plutôt opter pour un Many2many.
Particularité importante : le One2many n’est pas une colonne physique. C’est un champ virtuel qui s’appuie sur la présence d’un Many2one dans le modèle enfant pour effectuer la liaison.
Fonctionnement du champ
En base de données, rien n’est ajouté à la table du parent pour un One2many. Les relations sont matérialisées par une clé étrangère dans la table de l’enfant, via un champ Many2one qui pointe vers l’ID du parent.
Principe clé de l’ORM Odoo : chaque One2many correspond à un Many2one inverse sur le modèle lié. Le One2many n’est qu’une recherche inversée des enregistrements enfants dont le Many2one vaut l’ID du parent courant.
La relation One2many ↔ Many2one
Lors de la définition d’un One2many en Python, deux paramètres sont indispensables :
- comodel_name : le nom du modèle qui contient les enregistrements enfants.
- inverse_name : le nom du champ Many2one sur l’enfant qui renvoie vers le parent.
Par exemple, pour lier un contrat de service à ses livrables, on déclare un champ One2many sur le contrat qui pointe vers le modèle des livrables.
deliverable_ids = fields.One2many(
comodel_name='service.deliverable',
inverse_name='contract_id',
string='Deliverables'
)
Ici, contract_id est le Many2one défini sur service.deliverable qui référence le contrat. Sans ce Many2one, le One2many ne peut pas exister.
Ce que cela implique en base de données
La donnée effective des relations est stockée dans la table de l’enfant : chaque ligne enfant contient une colonne de clé étrangère pointant vers l’ID du parent. La table du parent ne reçoit pas de colonne supplémentaire pour le One2many.
Quand Odoo charge une fiche parent et doit afficher son One2many, l’ORM exécute une requête qui récupère toutes les lignes enfants dont la clé étrangère correspond à l’ID du parent — comportement standard des bases relationnelles, géré automatiquement par Odoo.
C’est pour cette raison que le One2many est considéré comme un champ calculé : il offre simplement une vue agrégée des enregistrements liés via l’inverse Many2one.
Cas d’usage en entreprise
Exemples pratiques courants
1. Commandes clients et positions
Dans le module Ventes, la commande (sale.order) possède un One2many order_line_ids vers sale.order.line. Chaque ligne contient produit, quantité, prix unitaire et remise, et se gère directement depuis la fiche commande.
2. Factures et lignes de facture
En comptabilité, le modèle account.move agrège ses lignes (account.move.line) via un One2many ; la facture calcule ses montants à partir des lignes enfants.
3. Projets et tâches
Le modèle project.project affiche ses tâches grâce à un One2many. Selon la vue vous verrez la liste, le kanban ou même le diagramme de Gantt, tous basés sur la même relation sous-jacente.
4. Entreprises et contacts liés
res.partner expose un champ child_ids listant les contacts liés à une société. Chaque contact porte un parent_id Many2one qui renvoie à l’entreprise.
5. Modèles personnalisés (service, production…)
Dans des modules sur-mesure, le One2many est l’outil naturel pour représenter des listes d’éléments dépendant d’un enregistrement : pièces d’un bon de maintenance, sessions d’un cours, livrables d’un contrat de service, etc.
Grâce à cette flexibilité, le One2many est omniprésent dans les personnalisations Odoo et facilite l’adaptation de l’ERP à des secteurs variés.
Créer ou personnaliser le champ
Deux méthodes principales existent pour créer un One2many : via Odoo Studio pour éviter le code, ou directement dans un module Python pour un contrôle total.
Créer un One2many avec Odoo Studio
Studio n’autorise pas la création d’un One2many directement depuis le parent si le Many2one inverse n’existe pas déjà sur l’enfant — la dépendance inverse est obligatoire.
Procédure recommandée dans Studio :
- Ouvrir d’abord le modèle enfant et y ajouter un champ Many2one pointant vers le parent.
- Sauver ce champ, puis revenir sur le modèle parent dans Studio.
- Créer un nouveau champ de type One2many : Studio vous demandera le modèle lié et le nom du champ inverse Many2one.
- Le One2many apparaît ensuite comme une liste intégrée dans la vue formulaire du parent.
Pour de nombreux utilisateurs métiers, c’est la méthode la plus simple pour établir des relations sans écrire de code ; le comportement est équivalent aux définitions Python.
Créer un One2many en Python (module personnalisé)
Pour les développeurs, définir le One2many dans un module offre plus de maîtrise sur le nommage, le comportement et les filtres (domain). Exemple complet :
from odoo import fields, models
class ServiceContract(models.Model):
_name = 'service.contract'
_description = 'Service Contract'
name = fields.Char(string='Contract Name', required=True)
deliverable_ids = fields.One2many(
comodel_name='service.deliverable',
inverse_name='contract_id',
string='Deliverables'
)
class ServiceDeliverable(models.Model):
_name = 'service.deliverable'
_description = 'Service Deliverable'
contract_id = fields.Many2one(
comodel_name='service.contract',
string='Contract',
ondelete='cascade'
)
name = fields.Char(string='Description', required=True)
Observez que le Many2one (contract_id) est défini sur le modèle enfant. L’inverse_name du One2many doit correspondre exactement au nom du champ Many2one.
Création via l’API XML-RPC
Si vous automatisez la gestion des champs ou administrez des bases Odoo à grande échelle, il est possible de créer des champs (dont One2many) via l’API XML-RPC en manipulant le modèle ir.model.fields. La règle reste identique : créer d’abord le Many2one puis référencer son nom dans le One2many via le paramètre relation_field.
Cette méthode est pratique pour déployer des personnalisations sur plusieurs environnements ou intégrer la configuration dans des scripts d’automatisation.
Bonnes pratiques
Bonnes pratiques issues de retours terrain
- Toujours créer le Many2one en premier. Qu’il s’agisse de Studio, de code Python ou de l’API, le champ inverse doit exister avant le One2many.
- Respecter la convention de nommage avec le suffixe
_ids. Noms commeline_ids,task_idsoudeliverable_idsindiquent immédiatement qu’il s’agit d’un recordset. - Préciser
ondelete='cascade'quand c’est pertinent. Si les enfants doivent disparaître quand le parent est supprimé, activez cette option sur le Many2one pour éviter des enregistrements orphelins. - Rester sélectif dans la liste intégrée. N’affichez que les colonnes utiles : trop d’informations alourdit la fiche et ralentit l’affichage. Utilisez des colonnes optionnelles pour les champs secondaires.
- Appliquer des
domainpour filtrer les enfants visibles. Quand un modèle enfant peut être partagé entre plusieurs parents, un domain permet de n’afficher que les enregistrements pertinents. - Gérer les grands volumes avec prudence. Si un parent peut posséder des milliers d’enfants, évitez de tout charger dans la vue formulaire : préférez une vue liste séparée, une pagination ou des recherches ciblées.
Pièges fréquents
Pièges et erreurs fréquentes
Oublier le Many2one inverse
L’erreur la plus fréquente est de définir un One2many sans que le Many2one correspondant existe sur le modèle enfant. Odoo lèvera une exception. Vérifiez toujours que l’inverse_name que vous indiquez est bien présent sur le modèle lié.
Mauvaise syntaxe d’écriture (write)
Pour modifier un One2many par code ou via l’API, il faut utiliser la syntaxe des tuples de commandes Odoo. On ne peut pas assigner une simple liste Python au champ One2many.
(0, 0, values_dict)pour créer un nouvel enfant.(1, child_id, values_dict)pour mettre à jour un enfant existant.(2, child_id, 0)pour supprimer un enfant et le supprimer définitivement.(4, child_id, 0)pour lier un enfant existant à la relation.(5, 0, 0)pour délier tous les enfants du parent sans les supprimer.
Tenter quelque chose comme record.line_ids = [1, 2, 3] ne fonctionnera pas ; il faut passer par les commandes Odoo adaptées.
Confondre One2many et Many2many
Un One2many implique une appartenance exclusive : chaque enfant n’a qu’un parent. Si un enregistrement doit être lié à plusieurs parents, utilisez un Many2many. Forcer un One2many dans ce cas vous obligera à dupliquer des enregistrements et nuira à l’intégrité des données.
Problèmes de performance sur de longues listes
Afficher des centaines ou des milliers de lignes dans un One2many ralentit fortement le chargement des formulaires — problème classique en comptabilité ou gestion des stocks. Utilisez des limites dans la vue, des filtres ou redirigez vers une liste dédiée pour les gros volumes.
Enregistrements orphelins après suppression
Si vous supprimez un parent sans avoir défini ondelete='cascade' sur le Many2one, les enfants peuvent rester avec une référence nulle ou invalide. À terme cela encombre la base et provoque des comportements inattendus dans les vues et rapports. Définissez une stratégie de suppression claire lors de la conception du modèle.
Conclusion
Le One2many est un pilier du modèle de données Odoo. Il alimente des fonctionnalités clefs — lignes de commande, lignes de facture, tâches de projet — et devient rapidement intuitif dès qu’on comprend son dépendance au Many2one inverse. Maîtriser ce couple relationnel rend la structure interne d’Odoo beaucoup plus lisible et extensible.
Que vous configuriez Odoo pour votre activité, que vous utilisiez Studio ou développiez un module sur-mesure, le One2many restera un outil courant. Savoir quand l’employer, comment le déclarer et comment éviter les erreurs classiques vous fera gagner du temps et préservera la qualité de vos données.
Pour approfondir, consultez les autres ressources et tutoriels de la collection « Données & API Odoo » pour des exemples pratiques et des guides techniques supplémentaires.
Besoin d’aide pour votre projet Odoo ?
Dasolo accompagne les entreprises dans l’implémentation, la personnalisation et l’optimisation d’Odoo selon leurs besoins. Que ce soit pour concevoir un modèle de données, développer un module, créer des champs ou tirer davantage de valeur de votre installation, notre équipe peut vous assister.
Si vous avez des questions sur votre projet Odoo ou souhaitez discuter de la meilleure façon d’organiser vos données, contactez-nous— nous serons ravis de vous aider.