Django : save() ne valide pas — le cycle complet de validation

Django : save() ne valide pas — le cycle complet de validation

Appeler obj.save() après avoir défini des validators et un clean() sur le modèle laisse croire que la validation est garantie. Elle ne l’est pas. Django ne déclenche pas full_clean() lors d’un save(), et ce comportement est délibéré. Comprendre pourquoi change la façon d’architecturer la validation dans un projet. Ce que save() fait réellement Le cycle de vie d’un save() est plus court que ce qu’on imagine : Signal pre_save envoyé field.pre_save() appelé sur chaque champ (auto_now, auto_now_add, etc.) INSERT ou UPDATE SQL selon la présence d’un pk Signal post_save envoyé Aucune validation n’y figure. Ni vérification de blank, ni max_length, ni appel à clean(). C’est aussi vrai pour Model.objects.create() et Model.objects.bulk_create() : les trois méthodes persistent sans valider. (bulk_create() est traité en détail dans Django in_bulk() et bulk_create() si vous travaillez sur des insertions en masse.) ...

18 mai 2026 · 5 min · Anthony
Vues matérialisées vs cache Django pour les requêtes lentes

Vues matérialisées vs cache Django pour les requêtes lentes

Le réflexe face à un endpoint de reporting lent, c’est souvent le cache. Un @cache_page, un cache.set(), et le problème disparaît… jusqu’à la prochaine expiration. Cette approche a une limite structurelle que les vues matérialisées PostgreSQL résolvent à la racine. Le problème du cache sur les endpoints analytiques Le cache Django stocke le résultat d’une vue Python. La requête SQL coûteuse s’exécute quand même à chaque expiration du cache. Pour un rapport construit sur plusieurs JOINs et agrégations, ça signifie que le premier utilisateur après chaque cache miss attend plusieurs secondes. ...

13 mai 2026 · 4 min · Anthony
Optimiser les requêtes Django ORM avec defer(), only() et Prefetch()

Optimiser les requêtes Django ORM avec defer(), only() et Prefetch()

Par défaut, Django charge tous les champs d’un modèle à chaque requête. Sur une vue liste de 50 articles, cela signifie 50 fois le contenu complet, le résumé, les métadonnées, les champs de traduction, même si on n’affiche que le titre et la date. Quatre méthodes QuerySet Django permettent de contrôler précisément ce qui est chargé : defer(), only(), values_list() et Prefetch(). Résultat : 2 requêtes SQL au lieu de N+2, avec uniquement les colonnes nécessaires. ...

8 mai 2026 · 6 min · Anthony
Django select_for_update() : verrouillage de lignes et concurrence

Django select_for_update() : verrouillage de lignes et concurrence

Deux requêtes simultanées lisent le stock d’un produit, constatent qu’il en reste un, et toutes les deux valident la commande. Le stock passe à -1. Ce type de condition de course est difficile à reproduire en développement et dévastateur en production. select_for_update() est la réponse de Django : poser un verrou SQL au moment de la lecture pour garantir qu’aucune autre transaction ne peut modifier la ligne avant la fin de l’opération. ...

6 mai 2026 · 4 min · Anthony
Renommer les champs Django ORM avec F() dans values()

Renommer les champs Django ORM avec F() dans values()

Quand on expose des données depuis un modèle Django vers une API ou un serializer, les noms de champs du modèle ne correspondent pas toujours à ce qu’on veut retourner. Le réflexe habituel : récupérer les instances, puis renommer en Python. Il existe une meilleure option : laisser la base de données faire le travail via F() dans values(). Le problème : les noms de champs s’imposent class Task(models.Model): name = models.CharField(...) created_at = models.DateTimeField(...) Si on veut retourner nom_tache au lieu de name, on récupère les données puis on renomme en Python, soit via une compréhension de dict, soit dans le serializer. Dans les deux cas, la transformation se fait après coup, en mémoire. ...

5 mai 2026 · 3 min · Anthony
Django Window Function vs GROUP BY : QuerySets chaînables

Django Window Function vs GROUP BY : QuerySets chaînables

Avec Django ORM, il existe deux façons d’ajouter une valeur calculée sur un ensemble de lignes : annotate() avec une agrégation classique (Max, Count, Sum…) ou annotate() avec une Window function. En surface, elles se ressemblent. En pratique, elles ont un comportement fondamentalement différent, et choisir la mauvaise peut bloquer toute la chaîne de filtrage. GROUP BY avec annotate() : des lignes qui s’écrasent Quand on combine values() et annotate() avec une agrégation, Django génère un GROUP BY en SQL. Le résultat : les lignes se regroupent, et on obtient une ligne par groupe. ...

4 mai 2026 · 4 min · Anthony
Django in_bulk() : optimiser les requêtes ORM et éviter le N+1

Django in_bulk() : optimiser les requêtes ORM et éviter le N+1

Quand on a une liste d’identifiants et qu’on veut récupérer les instances correspondantes, le réflexe habituel en Django c’est filter(pk__in=[...]). Ça marche, c’est une seule requête SQL. Mais in_bulk() est une optimisation ORM souvent ignorée : elle retourne un dictionnaire {id: instance} au lieu d’un QuerySet, ce qui change radicalement la façon d’accéder aux résultats. Là où filter() force un parcours O(n) pour retrouver un objet par son ID, in_bulk() donne un accès direct O(1). ...

4 mai 2026 · 4 min · Anthony

Newsletter

Reçois les nouveaux articles directement dans ta boite mail.

Pas de spam. Désabonnement en un clic.