Ewen Corre

2025-07-25

TIL #11 - Les querysets de Django

J'ai découvert la nouvelle fonctionnalité de Firefox permettant de grouper des onglets. Maintenant, au lieu d'avoir trop d'onglets, j'ai beaucoup trop d'onglets ouverts.

Parmi ces onglets se trouvent les apprentissages des dernières semaines. Dans cet article, je vais mettre les apprentissages qui concernent les objets QuerySet de Django.

Les filtres

range

Pour filtrer les objets dont la valeur se situe entre deux bornes, j'utilisais naïvement :

Entry.objects.filter(created_at__gt=start_date, created_at__lt=end_date)

C'était sans connaître range :

Entry.objects.filter(created_at__range=(start_date, end_date))

date

Si on veut filtrer une année en particulier, même pas besoin d'utiliser range !

Entry.objects.filter(created_at__year=2025)

Et ça marche aussi avec month !

Agrégation

Les agrégations permettent de générer des valeurs résumées dans des QuerySets. Cela permet d'annoter ces mêmes QuerySets avec ces valeurs, comme ceci :

Store.objects.annotate(min_price=Min("books__price"), max_price=Max("books__price"))

Un jour je m'arrachais les cheveux à faire une requête complexe avant de tomber sur cette partie de la documentation : Following relationships backwards. On peut utiliser les relations inverses (les related_name des ForeignKey des modèles) et dormir tranquille. 🎉

Transactions

J'ai récemment appris que le projet sur lequel je travaille a l'option de configuration ATOMIC_REQUEST=True. C'est à dire qu'à chaque requête HTTP, tout ce qui se passe dans une vue se produit dans une seule transaction SQL.

D'un côté ça permet de ne pas avoir de corruption de données : sans transaction, une première partie de la vue s'exécuterait, enregistrant des modifications en base de données, avant de planter sans enregistrer d'éventuelles autres modifications. D'un autre côté, si on a une erreur prévisible qu'on veut attraper, eh bien ça ne marche pas directement :

try:
    item.save()
except IntegrityError as e:
    print("Tantpy, on continue.")

C'est toute la transaction, toutes les requêtes de la vue qui plantent. Django renvoie l'erreur An error occurred in the current transaction. You can't execute queries until the end of the 'atomic' block.

Une solution est de remettre un transaction.atomic(), ce qui permet, en cas d'erreur dans la transaction interne, d'exécuter les requêtes suivantes dans la transaction principale :

try:
    with transaction.atomic():
        item.save()
except IntegrityError as e:
    print("Tantpy, on continue.")

Pour creuser, on peut lire la doc de psycopg : Nested transactions.