Makina Blog

Le blog Makina-corpus

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 :

Validation de la contrainte unique

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')
        ]
Erreur lors de l'insertion avec la contrainte unique avec condition

 

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à."
)

 

Contrainte unique avec condition

 

Tweet aventure de la contribution 2

 

Formations associées

Formations Python

Formation Python

Nantes Du 27 au 29 mai 2024

Voir la formation

Formations Django

Formation Django initiation

Nantes Du 12 au 14 mars 2024

Voir la formation

Formations Django

Formation Django avancé

À distance (FOAD) Du 9 au 13 décembre 2024

Voir la formation

Actualités en lien

Image
Django PostgreSQL
07/11/2023

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.

Voir l'article
Image
Encart Django
06/11/2023

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.

Voir l'article
Image
Agrégateur Geotrek
08/06/2023

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.

Voir l'article

Inscription à la newsletter

Nous vous avons convaincus