NXL Forge.Pendant que le marché rédige — nous livrons
Technique Odoo··4 min de lecture

Odoo 19 refuse _sql_constraints : passez à models.Constraint

L'attribut _sql_constraints, présent dans quantité de modules, n'est plus pris en charge par Odoo 19 : le module ne se charge pas tant qu'il subsiste. Voici la nouvelle syntaxe déclarative avec models.Constraint et la marche à suivre pour migrer.

Odoo 19ORMmigrationcontraintes SQLdéveloppement

Beaucoup de modules Odoo traînent depuis des années une liste de tuples nommée _sql_constraints. Elle sert à poser des contraintes au niveau de la base de données, comme l'unicité d'un code ou une borne sur un montant. Jusqu'à Odoo 18 incluse, cette écriture restait acceptée. En Odoo 19, elle ne l'est plus, et le module concerné ne se charge pas tant qu'on ne l'a pas migrée.

Ce que dit le code source

Le contrôle est explicite dans la branche 19.0. Le fichier odoo/orm/model_classes.py inspecte les attributs de chaque classe au moment de l'enregistrer dans le registre. Deux vérifications nous concernent directement 1.

La première cible l'ancien attribut de contraintes Python : « Model attribute '_constraints' is no longer supported, please use @api.constrains on methods instead. » La seconde cible les contraintes SQL : « Model attribute '_sql_constraints' is no longer supported, please define models.Constraint on the model. »

Le message ne parle pas d'avertissement à corriger plus tard. L'attribut n'est plus pris en charge. Si votre modèle le déclare encore, l'installation ou la mise à jour du module échoue au démarrage.

La nouvelle syntaxe déclarative

En Odoo 19, une contrainte de base de données se déclare comme un champ. On l'écrit sous forme d'attribut de classe dont le nom commence par un underscore, ce qui évite toute collision avec un nom de champ existant. La documentation officielle illustre le principe avec un contrôle de pourcentage 2 :

_check_percentage = models.Constraint(
    'CHECK(percentage >= 0 AND percentage <= 100)',
    "Le pourcentage doit être compris entre 0 et 100.",
)

Le premier argument porte la définition SQL. Le second porte le message affiché à l'utilisateur quand la contrainte est violée. Chaque contrainte devient un attribut distinct, au lieu d'être noyée dans une liste de tuples.

Pour l'unicité, la traduction est directe. Voici le passage d'une version à l'autre.

Odoo 18 et avantOdoo 19
_sql_constraints = [('name_uniq', 'unique(name)', 'Nom déjà utilisé')]_name_uniq = models.Constraint('UNIQUE(name)', 'Nom déjà utilisé')
_constraints = [...] (contraintes Python)méthode décorée par @api.constrains(...)

Index et unicité fine

La même logique s'étend aux index. Odoo 19 expose models.Index pour un index classique et models.UniqueIndex pour un index unique, toujours déclarés en attribut de classe 2. Cette séparation clarifie l'intention. Un UNIQUE(name) posé comme contrainte et un index unique partiel du type UNIQUE(company_id) WHERE active ne se lisent plus de la même manière, alors qu'ils se mélangeaient auparavant dans la liste de tuples.

Les pièges en migration réelle

Sur le terrain, quelques points méritent attention avant de lancer la mise à jour.

Le nom de l'attribut sert de référence. Un attribut mal nommé ou dupliqué dans deux classes héritées produit un comportement difficile à tracer, car c'est lui qui identifie désormais la contrainte côté Python.

Les contraintes Python passent par @api.constrains. Si votre module utilisait encore l'attribut _constraints, il faut le convertir en méthode décorée. Ce n'est pas une simple réécriture cosmétique, car la signature et le déclenchement diffèrent d'une liste de tuples.

La migration touche vos modules, pas seulement le cœur d'Odoo. Un module tiers de l'App Store qui n'a pas été mis à jour pour la 19 bloquera votre montée de version au même titre que votre code maison. Il vaut mieux auditer l'ensemble du dossier addons avant la bascule. Ce point rejoint les autres ruptures d'API que nous avions listées à propos des changements ORM d'Odoo 19.

Enfin, une contrainte d'unicité ne s'applique qu'aux données futures si des doublons existent déjà. La migration est le bon moment pour nettoyer les enregistrements en base, sans quoi la création de la contrainte SQL peut elle-même échouer.

Notre lecture

Ce changement n'apporte aucune fonctionnalité visible pour l'utilisateur final, mais il déplace un travail de correction sur les équipes techniques. Nous le voyons comme un signal cohérent avec la direction prise par Odoo depuis la 17 : rendre la déclaration des modèles plus explicite et plus proche de celle des champs. La bonne nouvelle est que la conversion est mécanique et se scripte en partie, un balayage des _sql_constraints dans un dépôt donnant vite la liste des modèles à reprendre. La moins bonne est qu'elle ne peut pas être reportée. Pour un projet qui vise la 19, nous traitons ces contraintes tôt dans le chantier de migration, avant de tester les vues et les rapports, car un module qui ne se charge pas bloque tout le reste.

Sources

  • Code source Odoo, branche 19.0, odoo/orm/model_classes.py : github.com/odoo/odoo/blob/19.0/odoo/orm/model_classes.py
  • Odoo 19.0 documentation, Server framework 101 – Chapter 10: Constraints : odoo.com/documentation/19.0/developer/tutorials/server_framework_101/10_constraints.html

Footnotes

  1. Messages de dépréciation lus dans la fonction add_to_registry() du fichier odoo/orm/model_classes.py de la branche 19.0 du dépôt officiel odoo/odoo.

  2. Syntaxe models.Constraint, models.Index et models.UniqueIndex et exemple _check_percentage, tels que documentés dans le tutoriel officiel Chapter 10: Constraints de la documentation Odoo 19.0. 2

Sur le même thème

Votre idée mérite un vrai logiciel

Une question, un projet Odoo ?

Modules préconfigurés, intégration, sur-mesure — parlons-en sans engagement.