Skip to content

Latest commit

 

History

History
473 lines (348 loc) · 13.3 KB

File metadata and controls

473 lines (348 loc) · 13.3 KB

🔝 Retour au Sommaire

11.6.1 Introduction à SQLAlchemy

Qu'est-ce que SQLAlchemy ?

SQLAlchemy est une bibliothèque Python qui facilite l'interaction avec les bases de données relationnelles. C'est ce qu'on appelle un ORM (Object-Relational Mapping), c'est-à-dire un outil qui permet de manipuler des données de base de données comme si c'était des objets Python.

Pourquoi utiliser SQLAlchemy ?

Sans SQLAlchemy, pour interagir avec une base de données, vous devriez écrire du code SQL brut :

# Sans SQLAlchemy (SQL brut)
cursor.execute("SELECT * FROM users WHERE age > 18")  
results = cursor.fetchall()  

Avec SQLAlchemy, vous manipulez des objets Python naturellement :

# Avec SQLAlchemy (ORM)
users = session.query(User).filter(User.age > 18).all()

Avantages principaux :

  • Code Python plus lisible et maintenable
  • Protection contre les injections SQL
  • Compatibilité avec plusieurs bases de données (SQLite, PostgreSQL, MySQL, etc.)
  • Validation automatique des données
  • Gestion simplifiée des relations entre tables

Installation

Pour installer SQLAlchemy, utilisez pip :

pip install sqlalchemy

Pour suivre ce tutoriel avec SQLite (base de données simple intégrée à Python), aucune installation supplémentaire n'est nécessaire.

Les deux façons d'utiliser SQLAlchemy

SQLAlchemy offre deux approches principales :

  1. SQLAlchemy Core : Niveau plus bas, proche du SQL mais avec Python
  2. SQLAlchemy ORM : Niveau plus haut, manipulation d'objets Python

Dans ce tutoriel, nous nous concentrerons sur l'ORM, qui est plus intuitif pour les débutants.

Concepts fondamentaux

Le moteur (Engine)

Le moteur est la connexion à votre base de données. C'est le point d'entrée de SQLAlchemy.

from sqlalchemy import create_engine

# Création d'une connexion à une base de données SQLite
# Le fichier 'ma_base.db' sera créé automatiquement
engine = create_engine('sqlite:///ma_base.db', echo=True)

Paramètres importants :

  • echo=True : Affiche toutes les requêtes SQL générées (utile pour apprendre)
  • echo=False : Mode production, n'affiche rien

Syntaxes de connexion courantes :

# SQLite (fichier local)
engine = create_engine('sqlite:///ma_base.db')

# SQLite (en mémoire, pour les tests)
engine = create_engine('sqlite:///:memory:')

# PostgreSQL
engine = create_engine('postgresql://user:password@localhost/ma_base')

# MySQL
engine = create_engine('mysql://user:password@localhost/ma_base')

La session

La session est votre espace de travail pour interagir avec la base de données. C'est comme un "panier" où vous placez vos modifications avant de les enregistrer définitivement.

from sqlalchemy.orm import sessionmaker

# Création d'une classe Session liée à notre moteur
Session = sessionmaker(bind=engine)

# Création d'une instance de session
session = Session()

Concept important : La session garde en mémoire vos changements et les envoie à la base de données uniquement quand vous le décidez (avec commit()).

Le modèle déclaratif (Base)

Pour définir nos tables, nous utilisons le système déclaratif de SQLAlchemy. Toutes nos classes de modèles hériteront d'une classe de base commune.

from sqlalchemy.orm import declarative_base

# Création de la classe de base
Base = declarative_base()

Premier exemple complet

Créons un modèle simple pour une table User :

from sqlalchemy import create_engine, Column, Integer, String  
from sqlalchemy.orm import declarative_base, sessionmaker  

# Configuration de base
Base = declarative_base()  
engine = create_engine('sqlite:///users.db', echo=True)  
Session = sessionmaker(bind=engine)  
session = Session()  

# Définition du modèle User
class User(Base):
    __tablename__ = 'users'  # Nom de la table dans la base de données

    # Définition des colonnes
    id = Column(Integer, primary_key=True)  # Clé primaire auto-incrémentée
    nom = Column(String(50), nullable=False)  # Chaîne de 50 caractères max, obligatoire
    email = Column(String(100), unique=True)  # Email unique
    age = Column(Integer)

    def __repr__(self):
        # Méthode pour afficher l'objet de manière lisible
        return f"<User(nom='{self.nom}', email='{self.email}', age={self.age})>"

# Création des tables dans la base de données
Base.metadata.create_all(engine)

Explication détaillée :

  • __tablename__ : Nom de la table en base de données
  • Column : Définit une colonne avec son type et ses contraintes
  • primary_key=True : Identifiant unique de chaque ligne
  • nullable=False : Le champ est obligatoire
  • unique=True : Chaque valeur doit être unique dans la table
  • __repr__ : Méthode spéciale pour afficher l'objet de façon lisible

Types de colonnes courants

SQLAlchemy offre de nombreux types de colonnes :

from sqlalchemy import Integer, String, Float, Boolean, DateTime, Text

class Exemple(Base):
    __tablename__ = 'exemples'

    id = Column(Integer, primary_key=True)
    nom = Column(String(100))           # Texte court (limite de caractères)
    description = Column(Text)           # Texte long (sans limite)
    prix = Column(Float)                 # Nombre décimal
    en_stock = Column(Boolean)           # True/False
    date_creation = Column(DateTime)     # Date et heure

Créer des enregistrements

Pour ajouter des données dans la base :

# Création d'un nouvel utilisateur
nouveau_user = User(nom="Alice Dupont", email="alice@example.com", age=28)

# Ajout à la session (préparation)
session.add(nouveau_user)

# Envoi à la base de données (sauvegarde définitive)
session.commit()

print(f"Utilisateur créé avec l'ID : {nouveau_user.id}")

Étapes importantes :

  1. Créer un objet Python
  2. L'ajouter à la session avec add()
  3. Confirmer avec commit()

Pour ajouter plusieurs enregistrements en une fois :

users = [
    User(nom="Bob Martin", email="bob@example.com", age=35),
    User(nom="Claire Petit", email="claire@example.com", age=42),
    User(nom="David Leroux", email="david@example.com", age=29)
]

session.add_all(users)  
session.commit()  

Lire des données (Requêtes simples)

Récupérer tous les enregistrements

# Récupérer tous les utilisateurs
tous_les_users = session.query(User).all()

for user in tous_les_users:
    print(user)

Récupérer un enregistrement par ID

# Récupérer l'utilisateur avec l'ID 1
user = session.get(User, 1)  
print(user)  

Filtrer les résultats

# Utilisateurs de plus de 30 ans
users_30_plus = session.query(User).filter(User.age > 30).all()

# Utilisateur avec un email spécifique
user = session.query(User).filter(User.email == "alice@example.com").first()
# .first() retourne le premier résultat (ou None si aucun)

Compter les enregistrements

# Nombre total d'utilisateurs
nombre_users = session.query(User).count()  
print(f"Nombre d'utilisateurs : {nombre_users}")  

Mettre à jour des données

# Récupérer un utilisateur
user = session.query(User).filter(User.nom == "Alice Dupont").first()

# Modifier ses attributs
user.age = 29  
user.email = "alice.dupont@example.com"  

# Sauvegarder les changements
session.commit()

SQLAlchemy détecte automatiquement les modifications et génère la requête SQL UPDATE appropriée.

Supprimer des données

# Récupérer un utilisateur
user = session.query(User).filter(User.nom == "Bob Martin").first()

# Supprimer de la session
session.delete(user)

# Confirmer la suppression
session.commit()

Gestion des transactions

Les transactions permettent de regrouper plusieurs opérations et de les annuler en cas d'erreur :

try:
    # Plusieurs opérations
    user1 = User(nom="Test1", email="test1@example.com", age=25)
    user2 = User(nom="Test2", email="test2@example.com", age=30)

    session.add(user1)
    session.add(user2)

    # Si tout se passe bien, on enregistre
    session.commit()

except Exception as e:
    # En cas d'erreur, on annule tout
    session.rollback()
    print(f"Erreur : {e}")

finally:
    # Fermeture de la session
    session.close()

Concepts clés :

  • commit() : Valide toutes les modifications
  • rollback() : Annule toutes les modifications depuis le dernier commit
  • close() : Ferme la session

Bonnes pratiques

1. Utiliser un context manager

Au lieu de gérer manuellement l'ouverture/fermeture des sessions :

from contextlib import contextmanager

@contextmanager
def get_session():
    session = Session()
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()

# Utilisation
with get_session() as session:
    user = User(nom="Exemple", email="exemple@example.com", age=25)
    session.add(user)
# La session est automatiquement fermée et committée

2. Valider les données avant insertion

def creer_utilisateur(nom, email, age):
    # Validations
    if not nom or len(nom) < 2:
        raise ValueError("Le nom doit contenir au moins 2 caractères")

    if age < 0 or age > 150:
        raise ValueError("L'âge doit être entre 0 et 150")

    # Création
    user = User(nom=nom, email=email, age=age)
    session.add(user)
    session.commit()
    return user

3. Éviter les requêtes N+1

Quand vous travaillez avec des relations entre tables (que nous verrons dans le prochain chapitre), faites attention aux requêtes répétées. SQLAlchemy offre des mécanismes d'optimisation comme le "eager loading".

Exemple complet d'application

Voici un exemple complet qui met tout ensemble :

from sqlalchemy import create_engine, Column, Integer, String  
from sqlalchemy.orm import declarative_base, sessionmaker  

# Configuration
Base = declarative_base()  
engine = create_engine('sqlite:///gestion_users.db', echo=False)  
Session = sessionmaker(bind=engine)  

# Modèle
class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    nom = Column(String(50), nullable=False)
    email = Column(String(100), unique=True, nullable=False)
    age = Column(Integer)

    def __repr__(self):
        return f"<User(id={self.id}, nom='{self.nom}', email='{self.email}')>"

# Création des tables
Base.metadata.create_all(engine)

# Fonction pour ajouter un utilisateur
def ajouter_utilisateur(nom, email, age):
    session = Session()
    try:
        user = User(nom=nom, email=email, age=age)
        session.add(user)
        session.commit()
        print(f"✓ Utilisateur {nom} ajouté avec succès!")
        return user
    except Exception as e:
        session.rollback()
        print(f"✗ Erreur : {e}")
    finally:
        session.close()

# Fonction pour lister tous les utilisateurs
def lister_utilisateurs():
    session = Session()
    try:
        users = session.query(User).all()
        if users:
            print("\n=== Liste des utilisateurs ===")
            for user in users:
                print(f"- {user.nom} ({user.email}) - {user.age} ans")
        else:
            print("Aucun utilisateur trouvé.")
    finally:
        session.close()

# Fonction pour rechercher un utilisateur par email
def rechercher_par_email(email):
    session = Session()
    try:
        user = session.query(User).filter(User.email == email).first()
        if user:
            print(f"Utilisateur trouvé : {user}")
        else:
            print(f"Aucun utilisateur avec l'email {email}")
        return user
    finally:
        session.close()

# Utilisation
if __name__ == "__main__":
    # Ajout d'utilisateurs
    ajouter_utilisateur("Marie Curie", "marie@science.com", 45)
    ajouter_utilisateur("Albert Einstein", "albert@physics.com", 76)
    ajouter_utilisateur("Ada Lovelace", "ada@coding.com", 36)

    # Liste
    lister_utilisateurs()

    # Recherche
    rechercher_par_email("marie@science.com")

Résumé des points clés

Configuration de base :

  1. Créer un engine (connexion à la base)
  2. Créer une Session (espace de travail)
  3. Définir une Base (classe parente des modèles)

Définition d'un modèle :

  • Hériter de Base
  • Définir __tablename__
  • Créer des colonnes avec Column()
  • Spécifier les types et contraintes

Opérations CRUD :

  • Create : session.add() puis session.commit()
  • Read : session.query().filter().all() ou .first()
  • Update : Modifier l'objet puis session.commit()
  • Delete : session.delete() puis session.commit()

Sécurité :

  • Toujours utiliser try/except avec rollback()
  • Fermer les sessions avec close()
  • SQLAlchemy protège automatiquement contre les injections SQL

Prochaines étapes

Maintenant que vous maîtrisez les bases de SQLAlchemy, vous êtes prêt à explorer :

  • Les relations entre tables (One-to-Many, Many-to-Many)
  • Les requêtes plus complexes (jointures, agrégations)
  • Les migrations de base de données
  • L'intégration avec des frameworks web (FastAPI, Flask)

SQLAlchemy est un outil puissant qui simplifie grandement le travail avec les bases de données. Avec ces fondamentaux, vous pouvez déjà créer des applications qui persistent des données de manière fiable et maintenable.

⏭️ Modèles et relations