Django select_related() vs prefetch_related() : le SQL généré

Django select_related() vs prefetch_related() : le SQL généré

select_related() ou prefetch_related() ? Les deux méthodes éliminent des requêtes N+1 dans Django, mais elles ne produisent pas du tout le même SQL. La première ajoute une jointure à la requête principale. La seconde exécute plusieurs requêtes, puis relie les objets en Python. Le bon choix dépend moins du volume de données que du type de relation traversée. Pour le comprendre, partons d’un modèle volontairement simple : from django.conf import settings from django.db import models class Post(models.Model): title = models.CharField(max_length=200) author = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="posts", ) tags = models.ManyToManyField("Tag", related_name="posts") class Comment(models.Model): post = models.ForeignKey( Post, on_delete=models.CASCADE, related_name="comments", ) body = models.TextField() is_public = models.BooleanField(default=True) author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) class Tag(models.Model): name = models.CharField(max_length=50) Depuis un Post, author désigne au maximum un utilisateur. En revanche, comments et tags peuvent contenir plusieurs objets. Cette différence de cardinalité dicte presque toujours la méthode à employer. ...

20 juillet 2026 · 8 min · Anthony
GitOps sans Kubernetes : Django avec doco-cd, SOPS et age

GitOps sans Kubernetes : Django avec doco-cd, SOPS et age

GitOps est presque toujours présenté avec Kubernetes en toile de fond : ArgoCD, Flux, des manifests par centaines. Résultat, beaucoup d’équipes qui déploient sur un simple VPS avec Docker Compose pensent que le modèle ne les concerne pas. Elles continuent à déployer en SSH : git pull, docker compose up -d, en croisant les doigts pour que personne n’oublie une étape. C’est dommage, parce que le cœur de GitOps n’a rien à voir avec Kubernetes. Et depuis quelque temps, un outil comble exactement ce vide : doco-cd, un moteur de déploiement continu pour Docker Compose. Combiné à SOPS et age pour chiffrer les secrets directement dans le repo, on obtient du GitOps sans Kubernetes : un pipeline complet où déployer se résume à git push. Cet article explique le principe, puis déroule la mise en place sur un projet Django avec plusieurs fichiers compose. ...

29 juin 2026 · 10 min · Anthony
CQRS en Django : read model dénormalisé sans Event Sourcing

CQRS en Django : read model dénormalisé sans Event Sourcing

Les quatre articles précédents de la série ont posé les briques pour qu’un système distribué reste cohérent : Saga pour orchestrer des workflows, Outbox pour publier des événements fiables, Inbox pour les consommer sans doublon, Idempotency Keys pour protéger l’API. Il manque une question : que fait-on des événements une fois publiés ? La réponse la plus fréquente est : on les utilise pour construire des vues de lecture. C’est exactement ce que propose le pattern CQRS (Command Query Responsibility Segregation) : séparer le modèle d’écriture du modèle de lecture, quand les deux divergent suffisamment pour que les forcer dans une même structure coûte plus cher que les dédoubler. ...

5 juin 2026 · 8 min · Anthony
Idempotency Keys : empêcher un client de payer deux fois

Idempotency Keys : empêcher un client de payer deux fois

Les deux articles précédents ont traité l’idempotence côté événement : le pattern Outbox garantit qu’un message est publié au moins une fois, et le pattern Inbox garantit qu’il n’est consommé qu’une fois. Reste un troisième endroit où le même problème se pose, plus en amont : l’API HTTP elle-même. Quand un client lance un POST /api/payments et que sa connexion lâche avant de recevoir la réponse, il ne sait pas si le paiement a été créé ou non. S’il retry, il risque de payer deux fois. S’il ne retry pas, il risque de ne pas payer du tout. Le pattern Idempotency Key, popularisé par Stripe et adopté depuis par la plupart des APIs de paiement, résout ce dilemme en mettant le contrôle du retry dans les mains du client. ...

4 juin 2026 · 8 min · Anthony
Pattern Inbox : consommer des événements sans les rejouer deux fois

Pattern Inbox : consommer des événements sans les rejouer deux fois

L’article précédent sur le Transactional Outbox a posé une garantie claire : tout événement écrit en base finira par être publié. Cette garantie est volontairement at-least-once. Un consommateur peut recevoir le même événement deux fois, trois fois, ou plus si le réseau se comporte mal. Le pattern Outbox ne promet jamais l’unicité. La conséquence est immédiate : si le consommateur fait deux fois l’effet du message, il facture deux fois, envoie deux emails, débite deux fois le stock. La cohérence garantie côté producteur s’effondre côté lecteur. ...

3 juin 2026 · 7 min · Anthony
Transactional Outbox : publier des événements sans perdre la cohérence

Transactional Outbox : publier des événements sans perdre la cohérence

Quand un service modifie sa base et veut prévenir le reste du système d’envoyer un événement à Kafka, RabbitMQ ou SQS, le code naïf ressemble à ça : on écrit en base, puis on publie. Si la publication échoue après la commit, l’événement est perdu. Si la publication réussit mais que la commit échoue, l’événement parle d’un état qui n’existe pas. Ces deux cas sont la définition du dual-write problem. ...

2 juin 2026 · 8 min · Anthony
Pattern Saga : gérer les transactions distribuées sans rollback

Pattern Saga : gérer les transactions distribuées sans rollback

Une opération métier qui touche plusieurs services pose un problème que SQL résout depuis cinquante ans à l’intérieur d’une base unique : que faire quand une étape réussit et que la suivante échoue ? Tant que tout vit dans la même base, BEGIN ... ROLLBACK suffit. Dès qu’on appelle un service externe, une API tierce ou une autre base, ce filet de sécurité disparaît. Le pattern Saga répond à cette question. Plutôt que de tenter une transaction ACID impossible, il découpe l’opération en étapes locales, chacune accompagnée d’une transaction compensatrice qui sait défaire son effet. Si l’étape 4 échoue, on rejoue en sens inverse les compensations des étapes 1, 2 et 3. ...

1 juin 2026 · 8 min · Anthony
Permissions déclaratives en DRF avec rest_access_policy

Permissions déclaratives en DRF avec rest_access_policy

Les permissions dans Django REST Framework fonctionnent, mais elles montrent leurs limites dès que les règles d’accès deviennent un peu complexes. Plusieurs rôles, des objets appartenant à un utilisateur, des actions custom sur un ViewSet : on se retrouve rapidement avec des classes has_permission et has_object_permission qui mélangent des vérifications hétérogènes, difficiles à lire et encore plus difficiles à tester. rest_access_policy (paquet djangorestframework-access-policy) propose une autre approche : déclarer les règles d’accès sous forme de statements, à la manière des politiques IAM d’AWS. Le résultat est lisible en un coup d’oeil, testable indépendamment du ViewSet, et extensible sans réécrire toute la classe. ...

26 mai 2026 · 7 min · Anthony
Hash, HMAC et chiffrement : sécuriser un token Django

Hash, HMAC et chiffrement : sécuriser un token Django

Une comparaison == sur un hash ne suffit pas à choisir le bon mécanisme. sha256, HMAC, hash salé, chiffrement : chaque approche offre des garanties différentes. Comprendre lesquelles change concrètement la façon de stocker et vérifier un token en Django. Hash simple import hashlib token_hash = hashlib.sha256(token.encode()).hexdigest() Un hash simple est déterministe : la même entrée produit toujours la même sortie. Aucun secret serveur n’est impliqué. Il est impossible de retrouver le token original depuis le hash (sha256 est une fonction à sens unique). Mais si quelqu’un connaît ou devine le token, il peut recalculer le hash et comparer. ...

25 mai 2026 · 4 min · Anthony
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

Newsletter

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

Pas de spam. Désabonnement en un clic.