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

Python

Python

À distance (FOAD) Du 13 au 15 février 2023

Voir la formation

Django

Django initiation

Toulouse Du 17 au 19 octobre 2022

Voir la formation

Django

Django avancé

A distance (foad) Du 7 au 9 novembre 2022

Voir la formation

Actualités en lien

Image
Django VuJs
06/10/2022 - 10:00

Comment développer et intégrer un composant VueJS indépendant dans Django ?

Si vous avez un besoin précis et complexe de JavaScript dans une page ou pour un widget, il peut être intéressant de développer en VueJS sans pour autant recourir à une SPA.

Voir l'article
Image
DjangoCon Europe 2022
21/09/2022 - 13:54

DjangoCon Porto 2022 : mise en œuvre du Domain-driven design (DDD) dans Django

Dans le cadre de la conférence DjangoCon Europe à Porto du 21 au 25 septembre 2022, nous présenterons notre retour d'expérience sur l'intégration de quelques concepts DDD dans OSIS, un projet open-source chez notre partenaire l'UCLouvain.

Voir l'article
Image
Randonnée
06/09/2022 - 09:43

Créer des vues SQL dans Django et les afficher dans un SIG

Nous allons décrire un processus via la mise en place de vues SQL qui permettent à l'utilisateur de lire de la donnée formatée, sans possibilité d'influer sur le contenu d'une base et tout en se connectant directement à celle-ci.

Voir l'article

Inscription à la newsletter

Nous vous avons convaincus