Makina Blog
L'aventure de la contribution : validation automatique des contraintes dans Django 4.1
J'ai eu l'occasion de contribuer une nouvelle fonctionnalité dans Django au cours de l'année dernière : la validation automatique des contraintes. Découvrez dans cet article, le processus de contribution suivi ainsi que le fonctionnement de cette nouveauté.
Comme beaucoup de contributions, le besoin m'est apparu pendant le développement d'un projet sur lequel je travaillais. J'ai eu besoin d'avoir une contrainte d'unicité un peu plus complexe que celle utilisée habituellement, et j'ai donc rajouté une condition dessus pour n'avoir la contrainte que sur certaines instances de mon modèle.
Petit exemple pour illustrer, imaginons que nous sommes en 4.0 avec le modèle suivant :
class User(models.Model):
username = models.CharField(max_length=50, unique=True)
active = models.BooleanField(default=True)
Lorsque nous essayons de créer un User avec un username déjà existant, le modèle valide que ce username n'est pas déjà présent en base de données. Cette information remonte dans les Form (et ModelForm) et est donc gérée automatiquement :
Maintenant, imaginons que nous ne voulons contraindre les utilisateurs actifs à avoir un username unique. Il suffit d'utiliser UniqueConstraint avec une condition, plutôt simple jusque là. Sauf que :
class User(models.Model):
username = models.CharField(max_length=50)
active = models.BooleanField(default=True)
class Meta:
constraints = [
UniqueConstraint('username', condition=Q(active=True), name='user_unique_active')
]
Boom Bada Boom. Django ne gérait absolument pas cette contrainte, à cause de la condition. La solution tout en gardant la même version fut de valider l'unicité en complétant la validation du modèle directement. Le problème était le suivant, j'avais beaucoup de modèles différents avec le même genre de contraintes. Ainsi, tous les modèles auraient du être modifiés et il aurait fallu penser à mettre à jour la validation si la contrainte un jour était changée. Pensez à vos tests unitaires de non régression dans ces cas là !
Je ne suis sûrement pas le premier à avoir eu ce besoin, et je ne serais certainement pas le dernier ! J'ai donc vérifié si une meilleure manière de faire existait.
Contribuer
Première chose à faire : tester sur la dernière version stable, puis sur la dernière version de développement (branche main du dépôt). Le problème étant toujours présent, les prochains arrêts sont le bug tracker et la mailing list. Je ne trouve pas ce qui m'intéresse, mais rapporter un problème c'est déjà contribuer. Début juin 2021, je décide donc de poster un message sur cette même mailing list pour me renseigner.
La réponse : un ticket existe bien créé en juin 2019, et faisant même mention d'un commentaire évoquant le sujet datant de 2012 ! S'ensuit de nombreux d'allers-retours donnant naissance à une première implémentation dans cette magnifique pull-request en juillet 2021.
Et c'est le début de la période la plus longue : relectures, refactoring, relances, validations par plusieurs développeurs, etc. Certaines fonctionnalités nécessaires mais indépendantes ont été extraites et commitées à part. La pull request est découpée en trois parties distinctes, permettant de l'intégrer progressivement. C'est finalement en mai 2022, presque un an après avoir commencé cette contribution, que la pull request est acceptée entièrement, juste à temps pour Django 4.1.
Profiter 🙂
Et si voir cette fonctionnalité dans les notes de mise à jour n'est pas une récompense suffisante, il suffit de mettre à jour son projet et d'en profiter !
UniqueConstraint(
'username',
condition=Q(active=True),
name='user_unique_active',
violation_error_message="Un utilisateur actif avec ce nom existe déjà."
)
Formations associées
Formations Django
Formation Django avancé
À distance (FOAD) Du 9 au 13 décembre 2024
Voir la formationActualités en lien
Utiliser des fonctions PostgreSQL dans des contraintes Django
Cet article vous présente comment utiliser les fonctions et les check constraints
PostgreSQL en tant que contrainte sur vos modèles Django.
Comment migrer vers une version récente de Django ?
Que ce soit pour avoir les dernières fonctionnalités ou les correctifs de sécurité, rester sur une version récente de Django est important pour la pérennité de son projet.
Le projet Agrégateur : fusionner des bases de données Geotrek
Le partage et la diffusion des données font partie des problématiques historiques au cœur du projet Geotrek.