Opérations sur les tables de base de données avec l'ORM de Django

Opérations fondamentales

Création de données

models.Produit.objects.create(nom='Clavier', prix=49.99)
# Insertion d'un enregistrement avec des arguments nommés

instance = models.Produit(nom='Souris', prix=19.99)
instance.save()
# Alternative avec instanciation et sauvegarde

objet, cree = models.Client.objects.get_or_create(
    nom='Martin',
    defaults={'email': 'martin@example.com', 'age': 30}
)
# Si l'enregistrement existe, il est récupéré ; sinon, il est créé avec les valeurs par défaut

objet, modifie = models.Client.objects.update_or_create(
    nom='Martin',
    defaults={'age': 31, 'statut': 'actif'}
)
# Si l'enregistrement existe, il est mis à jour ; sinon, il est créé

Lecture de données

models.Produit.objects.get(id=5)
# Récupération d'un seul objet ; lève une exception si inexistant

models.Produit.objects.all()
# Récupération de tous les objets

models.Produit.objects.filter(categorie='Électronique')
# Filtrage selon des conditions spécifiques

resultat = models.Produit.objects.filter(id=50).exists()
# Vérifie si des enregistrements correspondent ; retourne un booléen

Suppression de données

models.Produit.objects.filter(categorie='Informatique').delete()
# Suppression des objets correspondant aux critères

Mise à jour de données

models.Produit.objects.filter(categorie='Électronique').update(disponibilite=True)
# Mise à jour en masse des enregistrements filtrés

objet = models.Produit.objects.get(id=10)
objet.prix = 29.99
objet.save()
# Modification d'un objet individuel

Opérations avancées avec les filtres double underscore

Comptage

models.Produit.objects.filter(categorie='Électronique').count()
# Retourne le nombre d'objets correspondants

Filtres de comparaison

models.Produit.objects.filter(prix__gt=100)        # Prix supérieur à 100
models.Produit.objects.filter(prix__gte=50)       # Prix supérieur ou égal à 50
models.Produit.objects.filter(quantite__lt=10)     # Quantité inférieure à 10
models.Produit.objects.filter(quantite__lte=5)    # Quantité inférieure ou égale à 5
models.Produit.objects.filter(prix__lt=200, prix__gt=50)  # Prix entre 50 et 200

Filtres d'inclusion

models.Produit.objects.filter(id__in=[1, 2, 3])
# Sélectionne les objets dont l'id est dans la liste

models.Produit.objects.exclude(id__in=[4, 5, 6])
# Exclut les objets dont l'id est dans la liste

Filtres de nullité

models.Produit.objects.filter(description__isnull=True)
# Sélectionne les objets où le champ description est nul

Filtres de contenu

models.Produit.objects.filter(nom__contains='Pro')
# Recherche sensible à la casse avec le motif '%Pro%'

models.Produit.objects.filter(nom__icontains='pro')
# Recherche insensible à la casse

models.Produit.objects.exclude(nom__icontains='test')
# Exclusion basée sur un motif

Filtres de plage

models.Produit.objects.filter(prix__range=[20, 50])
# Sélectionne les objets dont le prix est dans la plage 20 à 50

Filtres de début et fin

models.Produit.objects.filter(nom__startswith='Ord')
# Commence par 'Ord'

models.Produit.objects.filter(nom__istartswith='ord')
# Commence par 'ord', insensible à la casse

models.Produit.objects.filter(nom__endswith='eur')
# Finit par 'eur'

Tri

models.Produit.objects.order_by('prix')
# Tri ascendant par prix

models.Produit.objects.order_by('-date_creation')
# Tri descendant par date de création

Agrégation et groupement

from django.db.models import Count, Avg

models.Produit.objects.values('categorie').annotate(total=Count('id'), prix_moyen=Avg('prix'))
# Groupement par catégorie avec comptage et moyenne des prix

Pagination

models.Produit.objects.all()[10:20]
# Limite et offset pour la pagination

Filtres par expression régulière

models.Produit.objects.filter(nom__regex=r'^[A-Z].*')
# Sélectionne les noms commençant par une lettre majuscule, sensible à la casse

models.Produit.objects.filter(nom__iregex=r'^[a-z].*')
# Insensible à la casse

Filtres temporels

models.Evenement.objects.filter(date_evenement__date='2023-01-01')
models.Evenement.objects.filter(date_evenement__year=2023)
models.Evenement.objects.filter(date_evenement__month=6)
models.Evenement.objects.filter(date_evenement__day=15)
models.Evenement.objects.filter(date_evenement__week_day=3)
models.Evenement.objects.filter(heure__hour=14)
models.Evenement.objects.filter(heure__minute=30)
models.Evenement.objects.filter(heure__second=0)

Opérations spéciales

Requêtes SQL brutes via extra()

models.Produit.objects.extra(
    select={'total_prix': 'SELECT SUM(prix) FROM details_commande WHERE produit_id = id'},
    where=['quantite > %s'],
    params=[10]
)
# Exécution de SQL personnalisé dans la requête

L'objet F pour les mises à jour de champs

from django.db.models import F

models.Produit.objects.update(quantite=F('quantite') + 5)
# Incrémentation de la quantité directement dans la base de données

L'objet Q pour les conditions complexes

from django.db.models import Q

requete = Q(prix__gt=100) | Q(categorie='Électronique')
models.Produit.objects.filter(requete)
# Filtre avec opérateur OU

requete_complexe = Q(Q(prix__lt=50) & Q(quantite__gt=0)) | Q(statut='promotion')
models.Produit.objects.filter(requete_complexe)
# Combinaison de conditions AND et OR

Exécution de SQL natif

from django.db import connection

curseur = connection.cursor()
curseur.execute('SELECT nom, prix FROM produits WHERE categorie = %s', ['Électronique'])
resultats = curseur.fetchall()
# Exécution directe de requêtes SQL pour des besoins spécifiques

Opérations de jointure entre tables

Définition des modèles

class ProfilUtilisateur(models.Model):
    utilisateur = models.OneToOneField('Utilisateur', on_delete=models.CASCADE)
    biographie = models.TextField(blank=True)
    avatar = models.URLField()

    def __str__(self):
        return self.utilisateur.nom

class Utilisateur(models.Model):
    TYPE_CHOICES = [
        ('ST', 'Standard'),
        ('PR', 'Premium'),
    ]
    type_utilisateur = models.CharField(max_length=2, choices=TYPE_CHOICES)
    nom = models.CharField(max_length=100)
    email = models.EmailField(unique=True)

    def __str__(self):
        return self.nom

class Groupe(models.Model):
    nom = models.CharField(max_length=50)
    membres = models.ManyToManyField('Utilisateur', related_name='groupes')

    def __str__(self):
        return self.nom

class Article(models.Model):
    titre = models.CharField(max_length=200)
    auteur = models.ForeignKey('Utilisateur', on_delete=models.CASCADE)
    groupe = models.ForeignKey('Groupe', on_delete=models.SET_NULL, null=True)

    def __str__(self):
        return self.titre

Relations un-à-un

utilisateur = models.Utilisateur.objects.get(id=1)
print(utilisateur.profilutilisateur.biographie)
# Accès au profil via la relation OneToOne

donnees = models.Utilisateur.objects.filter(id=1).values('email', 'profilutilisateur__avatar').first()
# Récupération de champs liés avec values()

Relations un-à-plusieurs

articles = models.Article.objects.filter(auteur__nom='Dupont')
# Filtrage des articles d'un auteur spécifique

for article in articles:
    print(article.titre)
# Itération sur les objets liés

Relations plusieurs-à-plusieurs

groupe = models.Groupe.objects.get(nom='Éditeurs')
utilisateur = models.Utilisateur.objects.get(nom='Durand')

# Ajout de membres au groupe
groupe.membres.add(utilisateur)
groupe.membres.add(*models.Utilisateur.objects.filter(type_utilisateur='PR'))

# Suppression de membres
groupe.membres.remove(utilisateur)

# Ajout depuis l'autre côté de la relation
utilisateur.groupes.add(groupe)

# Récupération des données liées
print(groupe.membres.all())
print(utilisateur.groupes.filter(nom='Administrateurs'))

Étiquettes: Django ORM Python SQL crud

Publié le 3 juin à 00h30