Requêtes avancées avec l’ORM Django : du filtrage simple aux jointures complexes

L’ORM de Django propose une API riche pour interroger la base sans écrire de SQL brut. Voici un panorama des outils les plus courants, illustrés par des exemples concrets.

13 méthodes indispensables

Méthode Retour Description rapide
all() QuerySet Tous les enregistrements
filter(**critères) QuerySet Lignes correspondant aux critères
get(**critères) Instance Une et une seule ligne
exclude(**critères) QuerySet Lignes ne correspondant pas aux critères
values(*champs) QuerySet de dict Colonnes choisies sous forme de dictionnaires
values_list(*champs) QuerySet de tuples Colonnes choisies sous forme de tuples
order_by(*champs) QuerySet Tri
reverse() QuerySet Tri inversé
distinct() QuerySet Doublons supprimés
count() int Nombre de lignes
first() Instance ou None Premier élément
last() Instance ou None Dernier élément
exists() bool Présence d’au moins un résultat

Différences entre objet unique et QuerySet

  • Instance de modèle : accès direct aux attributs (obj.nom), mais pas de update() ni de values().
  • QuerySet : ensemble d’instances, compatible values(), update(), slicing, etc.

Filtres via les doubles underscores

# intervalle
Article.objects.filter(id__range=(10, 20))

# inclusion
Article.objects.filter(statut__in=['publié', 'archivé'])

# texte
Article.objects.filter(titre__icontains='django')

# dates
Article.objects.filter(pub_date__year=2023)

Relations : navigation par objets

One-To-Many (Livre → Éditeur)

# sens direct
livre.editeur.nom

# sens inverse
editeur.livre_set.all()

One-To-One (Auteur → Profil)

auteur.profil.bio
profil.auteur.nom

Many-To-Many (Livre ↔ Auteur)

livre.auteurs.all()
auteur.livres.all()

Jointures via doubles underscores

# éditeur du livre « Django avancé »
Livre.objects.filter(titre='Django avancé').values('editeur__nom')

# livres parus chez « Eyrolles »
Editeur.objects.filter(nom='Eyrolles').values('livre__titre')

Manipulateurs de relations Many-To-Many

livre = Livre.objects.get(pk=1)
livre.auteurs.add(auteur1, auteur2)          # ou .set([id1, id2])
livre.auteurs.create(nom='Alice')            # crée et rattache
livre.auteurs.remove(auteur3)                # retire
livre.auteurs.clear()                        # vide

Optimisations

  • Lazy loading : la requête SQL n’est émise qu’au premier usage.
  • Cache : un QuerySet évalué est stocké en mémoire.
  • exists() : évite de charger toutes les lignes.
  • iterator() : parcourt les résultats sans les mettre en cache (utile pour gros volumes).

Agrégats

from django.db.models import Avg, Count, Max

Livre.objects.aggregate(prix_moyen=Avg('prix'))
Editeur.objects.annotate(nb_livres=Count('livre'))

Regroupements

# salaire moyen par département
Employe.objects.values('departement__nom').annotate(
    salaire_moyen=Avg('salaire')
)

F() et Q()

from django.db.models import F, Q

# comparaison de deux colonnes
Commande.objects.filter(quantite__gt=F('stock'))

# conditions complexes
Commande.objects.filter(
    Q(client='Dupont') | Q(montant__gt=1000),
    statut='payé'
)

Verrous et SQL brut

# verrouillage en lecture/écriture
Commande.objects.select_for_update().filter(client=u)

# requête SQL personnalisée
Personne.objects.raw(
    'SELECT * FROM personne WHERE age > %s',
    [18]
)

Étiquettes: Django ORM QuerySet filter annotate F

Publié le 31 août à 10h47