Ce que tu vas apprendre
_order
Définir l'ordre de tri par défaut dans toutes les vues et recherches.
_rec_name
Choisir le champ affiché dans les Many2one et le breadcrumb.
Constraint
Poser des contraintes SQL (UNIQUE, CHECK) en syntaxe v19.
Autres
_inherit, _auto, _log_access, _table…
- Avoir lu l'article T08 — Modèles de base
- Le module
odooskills_helpdeskinstallé (on le fait évoluer ici) - Un environnement Odoo 19 fonctionnel
odooskills_helpdesk de la version 19.0.1.0.0 à
19.0.1.1.0. On ajoute les attributs privés et une contrainte SQL
d'unicité sur la référence du ticket.
Les attributs privés d'un modèle, en un coup d'œil
À gauche, la classe Python. À droite, ce que chaque attribut change concrètement dans Odoo.
Les attributs privés Odoo commencent TOUS par
un underscore _. Ils ne sont pas des champs : ils configurent le modèle.
1 — _order : trier par défaut
Par défaut, Odoo trie les enregistrements par id ascendant — autrement dit
l'ordre d'insertion. C'est rarement ce qu'on veut. L'attribut _order
redéfinit le tri utilisé par toutes les vues (list, kanban, search)
et par tous les search() sans order= explicite.
Exemple — trier les tickets par priorité
Pour notre module helpdesk, on veut voir les tickets urgents en haut, puis par ordre chronologique inverse (plus récents en premier) :
class HelpdeskTicket(models.Model):
_name = 'helpdesk.ticket'
_description = 'Ticket Helpdesk'
_inherit = ['mail.thread', 'mail.activity.mixin', 'odooskills.helpdesk.mixin']
_order = 'priority desc, create_date desc'
La syntaxe est identique à SQL : liste de champs séparés par des virgules,
avec asc (défaut) ou desc optionnel sur chacun.
compute non stocké dans _order : l'ORM ne sait pas traduire
ça en SQL. Ajoute store=True sur le champ calculé ou trie sur un autre
champ.
2 — _rec_name : le champ "nom" du record
Chaque enregistrement Odoo a un nom affiché : c'est ce qui apparaît
dans les Many2one (champ relation), dans le breadcrumb et dans la fonction
display_name. Par défaut, Odoo cherche un champ name.
Si ton modèle n'a pas de champ name, il faut
soit en créer un, soit pointer _rec_name sur un autre champ :
# Cas 1 : champ name standard — _rec_name est implicite
class HelpdeskTicket(models.Model):
_name = 'helpdesk.ticket'
_rec_name = 'name' # par défaut — optionnel d'écrire cette ligne
name = fields.Char(required=True)
# Cas 2 : pas de champ name, on pointe ailleurs
class HelpdeskTicketHistory(models.Model):
_name = 'helpdesk.ticket.history'
_rec_name = 'event_type'
event_type = fields.Char(required=True)
ticket_id = fields.Many2one('helpdesk.ticket')
display_name — Pour construire un nom
combinant plusieurs champs (ex : "[TK-001] Panne imprimante"), surcharge la
méthode _compute_display_name() plutôt que de jouer sur _rec_name.
On verra le pattern dans l'article T12 (champs calculés).
3 — models.Constraint : les contraintes SQL en v19
Une contrainte SQL est une règle appliquée directement par PostgreSQL
(unicité, valeur positive, date cohérente, etc.). Elle est beaucoup plus
rapide et fiable qu'un @api.constrains Python car imposée en base,
pas dans le code.
Syntaxe Odoo 19 — nouvelle et obligatoire
class HelpdeskTicket(models.Model):
_name = 'helpdesk.ticket'
# Contrainte unique — attribut de classe préfixé par _
_unique_reference = models.Constraint(
'UNIQUE (reference)',
"La référence du ticket doit être unique.",
)
# Contrainte CHECK : valeur autorisée
_priority_check = models.Constraint(
"CHECK (priority IN ('0', '1', '2'))",
"Priorité invalide.",
)
reference = fields.Char(copy=False, index=True)
priority = fields.Selection([('0', 'Normale'), ('1', 'Haute'), ('2', 'Urgente')])
L'ancienne syntaxe _sql_constraints sous forme de
liste de tuples est totalement supprimée :
# ❌ Odoo 18 et avant — ne fonctionne PLUS en v19
_sql_constraints = [
('reference_unique', 'UNIQUE (reference)', "Doit être unique."),
]
# ❌ Même les nouvelles listes avec models.Constraint(...) sont supprimées
_constraints = [models.Constraint(...)] # erreur à l'install
# ✅ Odoo 19 — uniquement des attributs de classe séparés
_unique_reference = models.Constraint('UNIQUE (reference)', "Doit être unique.")
Vérification en base
Après un -u odooskills_helpdesk, PostgreSQL montre la contrainte :
psql -d odooskills_helpdesk_test -c "\d helpdesk_ticket" | grep -i unique
"helpdesk_ticket_unique_reference" UNIQUE CONSTRAINT, btree (reference)
Le nom de la contrainte en base est <table>_<nom_attribut>
— ici helpdesk_ticket_unique_reference. Odoo applique cette convention
automatiquement.
4 — _inherit : hériter de modèles existants
On l'a utilisé sans l'expliquer dans T08. _inherit prend une liste de
modèles dont on veut hériter les champs et méthodes. Il existe trois cas d'usage :
| Cas | Syntaxe | Effet |
|---|---|---|
| Extension classique | _inherit = 'res.partner' (sans _name) |
Ajoute des champs au modèle existant |
| Prototypage (delegation) | _inherits = {'res.partner': 'partner_id'} |
Nouveau modèle qui délègue à un autre via Many2one |
| Mixin | _inherit = ['mail.thread', 'mixin.name'] |
Injecte les champs/méthodes des mixins |
Dans notre module, on utilise la forme mixin pour ajouter
le chatter, les activités et les champs communs via notre AbstractModel :
class HelpdeskTicket(models.Model):
_name = 'helpdesk.ticket'
_inherit = [
'mail.thread', # chatter + messages
'mail.activity.mixin', # activités planifiées
'odooskills.helpdesk.mixin', # notre mixin custom (active, priority)
]
L'article T13 couvrira l'héritage en profondeur (extension de modèles existants, prototypage). Ici, retenir juste la forme mixin.
5 — Les autres attributs à connaître
| Attribut | Valeur par défaut | À quoi ça sert |
|---|---|---|
_table |
Déduit de _name |
Nom de table PostgreSQL custom (rarement changé) |
_auto |
True |
Créer automatiquement la table. False = tu la crées toi-même (vue SQL, table existante) |
_log_access |
True |
Ajouter create_uid, create_date, write_uid, write_date |
_abstract |
False |
Déclarer un modèle abstrait (mais préfère AbstractModel directement) |
_check_company_auto |
False |
Vérifier automatiquement la cohérence de company_id entre records liés |
_parent_name |
'parent_id' |
Nom du champ Many2one self-reference pour les hiérarchies |
_parent_store |
False |
Activer l'index MPTT pour parcourir les hiérarchies efficacement |
Ces attributs sont à connaître mais rarement à modifier. Les cas
_parent_* sont couverts dans l'article T14 (hiérarchie).
6 — Mettre à jour le module helpdesk
On applique tous ces attributs à nos deux modèles persistants. Voici les fichiers mis à jour :
models/helpdesk_ticket.py
from odoo import models, fields
class HelpdeskTicket(models.Model):
_name = 'helpdesk.ticket'
_description = 'Ticket Helpdesk'
_inherit = ['mail.thread', 'mail.activity.mixin', 'odooskills.helpdesk.mixin']
_order = 'priority desc, create_date desc'
_rec_name = 'name'
_unique_reference = models.Constraint(
'UNIQUE (reference)',
"La référence du ticket doit être unique.",
)
name = fields.Char(string='Sujet', required=True, tracking=True)
reference = fields.Char(string='Référence', copy=False, index=True)
description = fields.Text(string='Description')
# ... autres champs inchangés
models/helpdesk_ticket_category.py
from odoo import models, fields
class HelpdeskTicketCategory(models.Model):
_name = 'helpdesk.ticket.category'
_description = 'Catégorie de ticket helpdesk'
_order = 'sequence, name'
_rec_name = 'name'
_unique_name = models.Constraint(
'UNIQUE (name)',
"Le nom de la catégorie doit être unique.",
)
name = fields.Char(string='Nom', required=True)
sequence = fields.Integer(string='Séquence', default=10)
color = fields.Integer(string='Couleur')
__manifest__.py
On incrémente la version pour signaler l'évolution :
{
'name': 'OdooSkills Helpdesk',
'version': '19.0.1.1.0', # 1.0.0 → 1.1.0 : attributs + contrainte
# ... reste inchangé
}
Lancer la mise à jour
./odoo-bin -c config/odoo.conf -d odooskills_helpdesk_test \
-u odooskills_helpdesk --stop-after-init
Puis vérifier les contraintes et le champ reference en base :
psql -d odooskills_helpdesk_test -c "\d helpdesk_ticket" | grep -iE 'reference|unique'
reference | character varying | | |
"helpdesk_ticket__reference_index" btree (reference)
"helpdesk_ticket_unique_reference" UNIQUE CONSTRAINT, btree (reference)
Récapitulatif
| Attribut | Rôle | À mettre quand ? |
|---|---|---|
_name | Identifiant technique | Toujours |
_description | Libellé humain | Toujours (linter Odoo) |
_order | Tri par défaut | Dès que l'ordre naturel par ID ne convient pas |
_rec_name | Champ "nom" affiché | Si pas de champ name |
_inherit | Héritage / mixins | Presque toujours (mail.thread, activity…) |
_unique_* | Contrainte UNIQUE SQL | Pour garantir l'unicité en base |
Pour aller plus loin
- Article suivant (T10) — Les champs non-relationnels :
Char,Text,Integer,Float,Boolean,Date,Selection,Html,Binary. On ajoute 12 champs au ticket helpdesk - Article T12 — Contraintes Python (
@api.constrains) quand la règle ne peut pas s'exprimer en SQL - Ressources officielles :
Télécharge le Guide Technique Odoo 19
Architecture, pièges v19, checklist premier module — tout dans un PDF gratuit.
Télécharger le guide← Modèles de base Article suivant de la série : Champs non-relationnels →