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
Timing attacks en Django avec constant_time_compare

Timing attacks en Django avec constant_time_compare

Une comparaison == sur un token paraît anodine. En pratique, elle laisse filtrer une information mesurable : le temps d’exécution varie selon le nombre de caractères corrects. C’est le principe d’une timing attack, et c’est suffisant pour qu’un attaquant reconstitue le token caractère par caractère. Le problème : la comparaison qui s’arrête trop tôt Python compare les chaînes caractère par caractère et s’arrête dès qu’une différence est trouvée. def verify_token(request): user_token = request.GET.get('token') valid_token = "secret_abc123" if user_token == valid_token: return JsonResponse({'access': 'granted'}) return JsonResponse({'access': 'denied'}) Concrètement : ...

14 mai 2026 · 3 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 squashmigrations : nettoyer l'historique des migrations

Django squashmigrations : nettoyer l'historique des migrations

Après quelques mois de développement, il n’est pas rare d’accumuler 30, 50, voire 100 fichiers de migration sur une application Django. Chaque lancement de test qui repart d’une base vide les rejoue toutes. Chaque déploiement sur un nouvel environnement aussi. squashmigrations permet de fusionner plusieurs migrations en une seule, sans perdre la compatibilité avec les environnements déjà déployés. La commande et ce qu’elle génère La syntaxe prend une plage de migrations : ...

7 mai 2026 · 5 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

Newsletter

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

Pas de spam. Désabonnement en un clic.