Aller au contenu

Querysets et managers

L’ORM de Django (et donc, chacune des classes qui composent votre modèle) propose par défaut deux objets hyper importants:

  • Les managers, qui consistent en un point d’entrée pour accéder à la persistance des données,
  • Les querysets, qui permettent de filtrer des ensembles ou sous-ensemble d’objets. Les querysets peuvent s’imbriquer, pour ajouter d’autres filtres à des filtres existants, et fonctionnent comme un super jeu d’abstraction pour accéder à nos données (persistentes).

Ces deux propriétés vont de paire; par défaut, chaque classe de votre modèle, de part sa dépendance avec la classe models.Model, propose un attribut objects, qui correspond au manager, à partir duquel nous pourrons déclarer des filtres ou opérations à exécuter sur la base de données..

Managers

Comme nous le verrons à la section Services, les managers constituent une partie de la couche de “services” de notre application. Ces services peuvent se trouver à trois endroits :

. Dans les modèles ou au niveau des managers . Dans les forms ou les sérializeurs . Dans une couche de services séparée du reste.

Chaque définition de modèle utilise un Manager, afin d’accéder à la base de données et traiter nos demandes. Indirectement, une instance de modèle ne connait pas la base de données: c’est son gestionnaire qui a cette tâche. Il existe deux exceptions à cette règle: les méthodes save() et update().

Type d’opérationMéthode ou fonction associée
InstanciationMyClass()
CreateCréationMyClass.objects.create(...)
ReadRécupérationMyClass.objects.get(pk=...)
Filtres et querysetsMyClass.objects.all()
UpdateSauvegardemy_instance.save()
DeleteSuppressionmy_instance.delete()

Par défaut, le gestionnaire est accessible au travers de la propriété objects. Cette propriété a une double utilité:

  • Elle est facile à surcharger - il nous suffit de définir une nouvelle classe héritant de ModelManager, puis de définir, au niveau de la classe, une nouvelle assignation à la propriété objects
  • Il est tout aussi facile de définir d’autres propriétés présentant des filtres bien spécifiques.

Querysets

Les querysets permettent de réaliser des filtres complexes sur un ensemble structuré de données. Nous en avons déjà vu une partie au chapitre précédent, grâce à l’application de la méthode .filter(), à laquelle nous avions passé plusieurs paramètres permettant de rechercher des livres ou auteurs associés à notre modèle. Littéralement, un queryset est un ensemble de requêtes ; il s’agit effectivement d’une ou plusieurs requêtes - appliquées séquentiellement - et permettant d’obtenir un résultat correspondant à cet ensemble. Les querysets vont cependant plus loin, et permettent de réaliser des :

  1. Opérations logiques,
  2. Agrégations d’informations (Count, Max, …)
  3. Annotations exprimées au moyen de fonctions (également traduites en SQL …)
  4. … et plein d’autres sucreries syntaxiques (que nous n’aborderons pas ici 😴)

Opérations logiques

Les opérations logiques “de base” sont accessibles très facilement et peuvent être chaînées de manière totalement séquentielle :

"""Ceci nous donnera d'abord les livres qui contiennent le caractère "a"
dans leur titre, puis appliquera un deuxième filtre sur ceux
qui contiennent *également* le caractère "b"."""
Book.filter(title__contains="a").filter(title__contains="b") <1>
"Cette deuxième représentation est totalement identique à la première."""
Book.filter(title__contains="a", title__contains="b") <2>

Une troisième option consiste à utiliser les Q objects, que nous pouvons trouver dans l’espace de noms django.db. Ces éléments peuvent servir autant à appliquer un filtre AND que OR :

from django.db.models import Q
"""Nous cherchons les livres dont le titre contient "a" *ET* "b","""
Book.objects.filter(
Q(title__contains="a"),
Q(title__contains="b")
)
"""Nous cherchons ici les livres dont le titre contient "a" *OU* "b""""
Book.objects.filter(
Q(title__contains="a") | Q(title__contains="b")
) <2>

De la même manière, la négation peut être représentée via un Q object sur lequel nous appliquerons l’opérateur ~ :

from django.db.models import Q
"""Nous cherchons ici les livres dont le titre *ne contient pas* le caractère "a"."""
Book.objects.filter(~Q(title__contains="a")) <1>

Agrégations

https://docs.djangoproject.com/en/3.1/topics/db/aggregation/

Anotations

https://docs.djangoproject.com/en/3.1/topics/db/aggregation/