Retour au catalogue
Season 37 7 Épisodes 24 min 2026

SQLAlchemy

v2.0 — Édition 2026. Un cours audio complet sur SQLAlchemy, couvrant à la fois le Core et l'ORM, conçu pour la version 2.0 sortie en 2026. Apprenez à mapper votre domaine, structurer votre application, gérer les transactions avec la Session et exécuter des requêtes efficacement.

Bases de données ORM Fondamentaux de Python
SQLAlchemy
Lecture en cours
Click play to start
0:00
0:00
1
Les fondations : Qu'est-ce que SQLAlchemy et l'ORM ?
Bienvenue dans SQLAlchemy. Nous présentons l'architecture principale, en expliquant la différence entre le Core centré sur le schéma et l'ORM centré sur le domaine. Vous apprendrez le jargon de base et pourquoi vous avez besoin d'un ORM.
3m 39s
2
L'Engine : Votre passerelle vers la base de données
Toute application SQLAlchemy commence par l'Engine. Apprenez comment établir la connectivité, ce qu'est le connection pooling, et comment les dialectes et les DBAPIs font le lien avec votre base de données.
3m 21s
3
Mapper le domaine : Declarative Base et Modèles
Traduisez automatiquement vos classes Python en tables de base de données. Nous abordons DeclarativeBase, les types Mapped, et comment mapped_column construit les métadonnées de votre base de données.
3m 42s
4
Structure du projet : Organiser votre application
L'organisation du code est importante. Découvrez les bonnes pratiques pour structurer le dépôt d'un projet SQLAlchemy afin que votre engine, vos modèles et vos sessions restent propres et maintenables.
3m 38s
5
La Session : Maîtriser l'Unit of Work
Découvrez le pattern Unit of Work à travers la Session de l'ORM. Apprenez comment ajouter des objets, quand les flushes se produisent, et comment commiter les transactions à la perfection.
3m 24s
6
Requêter les données : Le construct Select moderne
Récupérez vos données exactement comme vous en avez besoin. Nous explorons le construct unifié select() de SQLAlchemy 2.0, le filtrage avec where(), et l'exécution de requêtes avec la session.
3m 06s
7
Faire le lien : Relations et JOINs
Liez vos tables de manière transparente. Apprenez à configurer les relations, à utiliser back_populates, et à gérer automatiquement les JOINs SQL entre les modèles liés.
3m 59s

Épisodes

1

Les fondations : Qu'est-ce que SQLAlchemy et l'ORM ?

3m 39s

Bienvenue dans SQLAlchemy. Nous présentons l'architecture principale, en expliquant la différence entre le Core centré sur le schéma et l'ORM centré sur le domaine. Vous apprendrez le jargon de base et pourquoi vous avez besoin d'un ORM.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. SQLAlchemy, épisode 1 sur 7. Ça t'est déjà arrivé de drop une table par accident ou de bousiller une query à cause d'une faute de frappe dans une string SQL brute ? Les queries concaténées avec des strings sont fragiles, difficiles à maintenir et posent un risque de sécurité. Cet épisode couvre la solution : Les fondations : c'est quoi SQLAlchemy et l'ORM ? Le problème principal que rencontrent les développeurs quand ils écrivent des applications basées sur une base de données, c'est que Python et les bases de données relationnelles pensent différemment. Python utilise des objets. Les objets ont un état, un comportement et des relations complexes. Les bases de données relationnelles utilisent des tables, des colonnes et des lignes. Elles reposent sur des clés primaires et des clés étrangères. Mettre un objet Python dans une table de base de données nécessite de traduire son état en un statement SQL. Le récupérer nécessite de parser les lignes et de repeupler un nouvel objet. Cette friction, c'est ce qu'on appelle l'object-relational impedance mismatch. Écrire des strings SQL brutes pour gérer cette traduction, c'est fastidieux et source d'erreurs. SQLAlchemy est un toolkit SQL pour Python conçu pour résoudre exactement ce problème. Contrairement à certains outils qui essaient de cacher la base de données derrière un mur d'abstraction, SQLAlchemy embrasse le SQL. Il offre un workflow robuste et Pythonic qui génère du SQL optimal tout en te gardant aux commandes. L'architecture est divisée en deux parties distinctes : le Core et l'ORM. Le Core de SQLAlchemy, c'est la fondation. Il est orienté commandes et orienté schéma. Ça veut dire que tu travailles directement avec les tables, les colonnes et les lignes de la base de données, mais tu le fais en utilisant des objets Python au lieu de strings de texte brutes. Si tu veux sélectionner des données, tu appelles une méthode select. Si tu veux filtrer, tu chaines une méthode where. Ça élimine le risque d'erreurs de syntaxe lié à la concaténation de strings et ça protège contre les injections SQL. Le Core est essentiellement un langage d'expressions SQL. L'ORM, ou Object Relational Mapper, vient se placer au-dessus du Core. Alors que le Core est orienté schéma, l'ORM est orienté état et domaine. Voici l'idée clé. Avec l'ORM, tu arrêtes de penser en termes de tables et de lignes, et tu commences à penser au modèle de domaine de ton application. Tu définis une classe Python standard, par exemple, une classe user. Tu configures l'ORM pour mapper cette classe à une table user dans la base de données, et mapper les attributs de la classe aux colonnes de la table. Quand tu fais une query sur la base de données avec l'ORM, tu ne récupères pas des lignes de données brutes. Tu récupères des instances complètement peuplées de ta classe user. L'ORM gère la traduction de façon transparente. Plus important encore, il tracke l'état de ces objets. Si tu changes un nom d'utilisateur en Python, l'ORM remarque que l'objet a été modifié. Quand tu dis à la session de la base de données de sauvegarder tes changements, l'ORM trouve automatiquement les bons statements d'update et les exécute via le Core. Tu te concentres sur tes objets Python, et l'ORM gère la synchronisation avec la base de données. La chose la plus importante à retenir, c'est que l'ORM n'est pas une boîte noire qui remplace le Core. C'est une couche optionnelle construite entièrement à partir de composants du Core, ce qui te permet de redescendre sans problème vers de la génération SQL explicite dès que ta logique de domaine exige des performances maximales. Si tu trouves ces épisodes utiles et que tu veux soutenir l'émission, tu peux chercher DevStoriesEU sur Patreon. Merci d'avoir passé quelques minutes avec moi. À la prochaine, prends soin de toi.
2

L'Engine : Votre passerelle vers la base de données

3m 21s

Toute application SQLAlchemy commence par l'Engine. Apprenez comment établir la connectivité, ce qu'est le connection pooling, et comment les dialectes et les DBAPIs font le lien avec votre base de données.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. SQLAlchemy, épisode 2 sur 7. Tu veux parler à une base de données, mais les différentes bases de données parlent des dialectes différents, et les drivers Python ont tous leurs propres spécificités. Il te faut un composant unique et unifié qui gère le connection pool, la traduction des dialectes et la gestion des drivers sans faire déborder ces détails dans la logique de ton application. Ce composant, c'est l'Engine. L'Engine est le point de départ de toute application SQLAlchemy. Il sert de registre central et de factory pour les connexions à la base de données. Quand ton application a besoin de parler à la base de données, elle passe au final par l'Engine. En coulisses, l'Engine maintient un connection pool. Cela signifie qu'il garde un cache de connexions actives à la base de données, les maintenant ouvertes et prêtes à être utilisées. Réutiliser les connexions d'un pool est beaucoup plus rapide que de négocier une toute nouvelle connexion réseau à chaque fois que tu as besoin d'exécuter une query. Pour instancier un Engine, tu utilises une fonction appelée create engine. Cette fonction nécessite une URL de base de données. L'URL est une string qui fournit l'emplacement de la base de données, les identifiants, et deux éléments de configuration critiques : le dialecte et la DBAPI. Le dialecte est la famille de base de données que tu cibles. SQL est un standard, mais chaque database engine l'implémente de manière légèrement différente. Le dialecte indique à SQLAlchemy s'il est en train de formater des queries pour PostgreSQL, MySQL, Oracle ou SQLite. Il gère toutes les variations spécifiques aux fournisseurs pour que ton code Python puisse rester cohérent. Voici le point clé. SQLAlchemy ne se connecte pas directement à ta base de données. Il délègue toujours la communication réseau réelle à un driver Python tiers. Ce driver est connu sous le nom de DBAPI. Si tu utilises PostgreSQL, ta DBAPI pourrait être psycopg2. Si tu utilises SQLite, c'est généralement pysqlite. L'URL de la base de données spécifie ces deux éléments ensemble. Par exemple, l'URL pourrait commencer par la string sqlite plus pysqlite. Cela dit explicitement à l'Engine de formater les queries en utilisant le dialecte SQLite et de les transmettre en utilisant le driver pysqlite. Regardons comment configurer une base de données SQLite in-memory. C'est un pattern très courant pour les tests car la base de données vit entièrement dans la RAM et disparaît quand le programme se termine. Tu appelles la fonction create engine et tu lui passes ta string d'URL. Pour une base de données in-memory, l'URL est sqlite plus pysqlite deux points slash slash slash deux points memory deux points. Quand tu exécutes cette ligne de code, l'objet Engine est créé, mais il ne se connecte pas immédiatement à la base de données. L'Engine est lazy. Il prépare la configuration et met en place le connection pool, mais il attend la toute première fois que tu lui demandes explicitement d'exécuter une tâche avant de contacter réellement la DBAPI pour établir une connexion. Pendant le développement, tu as souvent besoin de vérifier exactement ce que SQLAlchemy envoie à la base de données. La fonction create engine accepte un paramètre optionnel appelé echo. Si tu mets echo à true, l'Engine va logger tous les statements SQL bruts qu'il génère directement sur ta standard output. Il agit comme un outil de débogage intégré, te permettant de voir la traduction exacte entre tes opérations Python et le SQL qui en résulte. L'Engine existe pour abstraire la réalité complexe des connexions réseau et des drivers de base de données. Il donne à ton application une interface propre et stable, s'assurant que le reste de ton code n'ait jamais à se soucier de la façon dont la base de données est physiquement atteinte. Merci d'avoir passé ce moment avec moi. J'espère que tu as appris quelque chose de nouveau.
3

Mapper le domaine : Declarative Base et Modèles

3m 42s

Traduisez automatiquement vos classes Python en tables de base de données. Nous abordons DeclarativeBase, les types Mapped, et comment mapped_column construit les métadonnées de votre base de données.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. SQLAlchemy, épisode 3 sur 7. Tu écris tes modèles de domaine Python, et ensuite tu dois écrire un tas de SQL séparé juste pour créer les tables qui vont les contenir. Avec le temps, ces deux définitions séparées finissent presque toujours par se désynchroniser. Et si tes classes Python pouvaient concevoir automatiquement le schéma de ta base de données ? C'est exactement ce qu'on résout aujourd'hui avec Mapping the Domain : Declarative Base et Models. Avant de regarder les classes elles-mêmes, on doit parler des metadata de la base de données. Dans SQLAlchemy, les metadata sont en fait un catalogue structurel. C'est un registry Python central qui stocke le blueprint exact de ta base de données. Il traque chaque table, chaque colonne et chaque contrainte que tu définis. Chaque fois que tu mappes une classe dans SQLAlchemy, les détails de cette classe alimentent directement ce catalogue unique. Pour connecter tes classes Python à ce catalogue de metadata, tu utilises une construction appelée DeclarativeBase. Tu n'utilises pas DeclarativeBase directement. À la place, tu crées ta propre classe de base custom en héritant de celle-ci. À partir de là, chaque modèle que tu construis dans ton application héritera de ta classe de base custom. Dès qu'une classe en hérite, SQLAlchemy enregistre discrètement ce nouveau modèle dans le catalogue de metadata sous-jacent. Prends l'exemple de la création d'un modèle User. Tu définis une classe Python nommée User et tu hérites de ta classe de base custom. La première chose que tu définis dans cette classe, c'est un attribut nommé tablename, écrit avec des double underscores de chaque côté. Tu le définis sur la string user_account. Cette assignation explicite dit exactement à SQLAlchemy quelle table de base de données sous-jacente va stocker ces objets Python. Ensuite, tu établis les colonnes. SQLAlchemy 2.0 se base sur les type hints standard de Python pour faire ça. Tu définis tes attributs en utilisant une annotation spéciale appelée Mapped. Pour un identifiant, tu annotes ton attribut ID comme un Mapped contenant un integer. Pour un username, tu l'annotes comme un Mapped contenant une string. Ce type hint est strictement pour Python. Il dit à ton IDE et à ton type checker quelles données attendre. Mais le moteur de base de données a besoin d'instructions plus spécifiques qu'un simple type Python. C'est là qu'une fonction appelée mapped_column entre en jeu. Tu assignes mapped_column à ton attribut de classe, et à l'intérieur de ses parenthèses, tu configures les règles spécifiques à la base de données. Pour ton user ID, tu appelles mapped_column et tu passes un flag qui le marque explicitement comme la primary key. Pour une colonne string, tu pourrais passer une limite maximale de caractères ou un flag exigeant que le champ soit unique. Si ton type hint Python fournit tout le contexte dont SQLAlchemy a besoin, tu peux carrément omettre mapped_column. SQLAlchemy va déduire une colonne de base de données basique et standard directement à partir de l'annotation Mapped. Mais pour les primary keys, ou n'importe quelles contraintes spécifiques de base de données, mapped_column est strictement requis. Voici l'idée clé. Une fois que tes classes Python sont écrites, le design de ton schéma est déjà terminé. Le catalogue de metadata contient maintenant une map parfaite de ton domaine. Tu peux appeler une méthode nommée create_all sur les metadata de ta classe de base, en lui passant ton moteur de base de données. SQLAlchemy regarde ta classe User, lit les colonnes mappées, les traduit instantanément en statements CREATE TABLE parfaitement formatés, et les applique. Exactement le même code Python que tu écris pour faire tourner ton application devient la single source of truth qui construit la structure de ta base de données. Merci d'avoir été là. J'espère que tu as appris quelque chose de nouveau.
4

Structure du projet : Organiser votre application

3m 38s

L'organisation du code est importante. Découvrez les bonnes pratiques pour structurer le dépôt d'un projet SQLAlchemy afin que votre engine, vos modèles et vos sessions restent propres et maintenables.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. SQLAlchemy, épisode 4 sur 7. Tu as écrit tes models, ton engine et tes queries. Mais tout balancer dans un seul fichier, c'est la voie rapide vers un cauchemar de maintenance et des circular imports. La solution, c'est le Project Layout : structurer ton application. Quand tu regardes des tutoriels, le code est généralement contenu dans un seul script. Tu y vois le setup de l'engine, la base class, les models et les queries empilés de haut en bas. Ce design est super pour apprendre la syntaxe, mais si tu essaies de construire un système en production de cette manière, il s'effondre. Dès que tu ajoutes des web routes ou des background workers, les problèmes commencent. Si une web route doit importer un model User, mais que ce model se trouve dans le même fichier qui démarre l'application server ou initialise la connexion à la base de données, Python est perdu. Tu te retrouves avec des circular imports et ton code devient impossible à tester. Tu as besoin d'une stricte separation of concerns. Le pattern standard consiste à diviser ce script unique en modules distincts en fonction de leur job. Tout d'abord, tu crées un fichier dédié, généralement appelé database point py. Ce fichier a exactement une seule responsabilité : gérer la connexion à la base de données. C'est là que tu places ton appel à create engine et que tu setup ton session maker. Tu n'y définis pas de tables et tu n'y lances pas de queries. En isolant l'engine, tu t'assures que ton application ne crée qu'un seul connection pool. N'importe quel autre module de ton projet peut importer le session maker depuis ce fichier en toute sécurité, sans déclencher accidentellement la logique de démarrage de l'application. Ensuite, tu déplaces les définitions de tes tables dans un fichier appelé models point py. Ce fichier contient ton Declarative Base et toutes tes mapped classes, comme un objet User ou Address. Il n'importe rien depuis ton fichier database. C'est la partie qui compte. Tes models définissent la forme de tes données, mais ils ne savent pas comment se connecter à la base de données. Comme models point py n'a aucune dépendance envers l'engine ou la session active, tu peux importer tes mapped classes n'importe où dans ton application sans te soucier des side effects. Si ton projet devient volumineux, tu peux même séparer les models dans un répertoire avec des fichiers distincts pour différents domaines. Tant qu'ils importent tous exactement la même instance de Declarative Base, SQLAlchemy sait qu'ils appartiennent à la même collection de metadata. Enfin, il te faut un endroit pour réellement exécuter tes queries. Tu gardes l'exécution des queries totalement séparée de tes models et de tes fichiers de connexion. Généralement, ça va dans tes route handlers ou dans des fichiers de service dédiés. Quand une application a besoin de données, le fichier de service importe le session maker depuis ton module database, et les mapped classes requises depuis ton module models. Il ouvre une session à l'aide d'un context manager, exécute un select statement, récupère les objets et laisse le context manager fermer la connexion en toute sécurité une fois le bloc terminé. Le fichier de service agit comme coordinateur. Structurer ton projet de cette manière garde ta logique de connexion isolée, tes data schemas portables et tes business operations propres. La règle la plus importante à retenir est que tes data models ne doivent jamais importer ton database engine. C'est tout pour cet épisode. Merci pour ton écoute, et continue à développer !
5

La Session : Maîtriser l'Unit of Work

3m 24s

Découvrez le pattern Unit of Work à travers la Session de l'ORM. Apprenez comment ajouter des objets, quand les flushes se produisent, et comment commiter les transactions à la perfection.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. SQLAlchemy, épisode 5 sur 7. Imagine une zone de transit où tu peux mettre en file d'attente des dizaines de modifications de base de données et toutes les push parfaitement en même temps, ou les annuler instantanément. C'est exactement le problème que résout le concept de cet épisode : La Session : Maîtriser l'Unit of Work. La Session SQLAlchemy est le moyen principal par lequel tes objets Python communiquent avec la base de données. Elle fonctionne selon un pattern appelé l'Unit of Work. Quand tu utilises la Session, tu n'envoies pas de commandes individuelles directement à la base de données. Tu interagis avec un espace de travail intelligent. La Session surveille tes opérations, rassemble les ajouts, les modifications et les suppressions, et calcule la façon la plus efficace de les traduire en SQL au bon moment. À l'intérieur de cet espace de travail se trouve un mécanisme appelé l'Identity Map. C'est un dictionnaire interne qui lie la clé primaire d'une ligne de base de données à l'adresse mémoire spécifique de ton objet Python. Si tu demandes un utilisateur avec l'ID cinq, la Session vérifie d'abord l'Identity Map. Si l'objet est déjà chargé, tu récupères exactement la même instance Python. Ça te garantit de ne jamais avoir deux objets Python distincts qui représentent la même ligne de base de données en même temps. Regardons comment insérer de nouvelles données. Tu commences par créer une instance de ta classe User mappée, en lui donnant un nom comme Alice. À ce stade, ton objet est dans un état appelé transient. Il existe uniquement dans la mémoire Python. La base de données n'a aucune idée de son existence, et la Session n'en est pas consciente. Pour lier l'objet à ton espace de travail, tu le passes à la méthode add de la Session. L'objet passe dans un état appelé pending. La Session a enregistré ton intention d'insérer ce nouvel utilisateur, mais elle n'a encore envoyé aucun SQL sur le réseau. L'objet attend simplement dans la queue. Ça introduit la différence stricte entre un flush et un commit. Quand tu appelles flush sur la Session, elle prend ta queue pending et push les changements dans la transaction de base de données actuelle. Elle émet l'instruction SQL INSERT. La base de données l'exécute et assigne une clé primaire. De retour dans ton code Python, ton objet utilisateur est instantanément peuplé avec ce nouvel ID de base de données. L'objet est passé à un état persistent. Voici le point clé. Même si l'objet est persistent et a un ID, le changement n'est toujours pas permanent. La base de données isole cette transaction. Supposons que ton code détecte soudainement une erreur, comme une adresse e-mail invalide pour l'utilisateur que tu viens de flush. Comme tu n'as pas fait de commit, tu peux faire un rollback. La base de données annule entièrement la transaction, et rien n'est sauvegardé dans les tables réelles. Quand tu es absolument certain que les données sont correctes, tu appelles commit. Un commit finalise la transaction et sauvegarde les données sur le disque. Appeler commit déclenche automatiquement un flush en premier, donc tu n'as généralement pas besoin d'appeler flush manuellement, sauf si tu as spécifiquement besoin de lire une clé primaire générée avant de terminer le reste de ta logique. La Session agit comme un buffer entre la mémoire de ton application et le stockage permanent, te permettant d'orchestrer des changements de données complexes en toute sécurité avant de les verrouiller dans la réalité. Merci d'avoir écouté. Prenez soin de vous, tout le monde.
6

Requêter les données : Le construct Select moderne

3m 06s

Récupérez vos données exactement comme vous en avez besoin. Nous explorons le construct unifié select() de SQLAlchemy 2.0, le filtrage avec where(), et l'exécution de requêtes avec la session.

Télécharger
Salut, ici Alex de DEV STORIES DOT EU. SQLAlchemy, épisode 6 sur 7. Dans les anciennes versions de SQLAlchemy, écrire des requêtes donnait l'impression d'avoir le cerveau coupé en deux. Tu devais mémoriser une syntaxe pour les opérations Core et un ensemble de règles complètement différent pour l'ORM. À chaque fois que tu écrivais une requête, tu devais t'arrêter et te demander si tu récupérais des lignes brutes ou des objets mappés. La version 2.0 a effacé cette frontière avec le construct select moderne. Le concept est simple. Il y a maintenant une seule fonction unifiée appelée select. Tu lui passes directement l'entité que tu veux requêter. Si tu as une classe ORM nommée User, tu passes la classe User à la fonction select. Ça crée un objet select qui représente une requête sur la table user sous-jacente. Cet objet select est génératif. Appeler la fonction select n'exécute rien sur la base de données. À la place, ça crée un objet expression en mémoire. Quand tu appelles une méthode sur cet objet pour ajouter des conditions, il te retourne un nouvel objet select avec ces nouvelles conditions appliquées. Ça te permet de construire des requêtes complexes dynamiquement, étape par étape, en passant l'objet un peu partout dans ton application avant même de parler à la base de données. Voilà pour la requête de base. Comment est-ce que tu la filtres ? Tu ajoutes la méthode where à ton objet select. À l'intérieur de la méthode where, tu utilises les opérateurs Python standards directement sur les attributs de ta classe mappée. Par exemple, pour trouver un user spécifique, tu tapes la classe User point name, suivi d'un double signe égal, et de la string cible. SQLAlchemy intercepte ça. Il surcharge les opérateurs Python standards comme le double égal ou le signe supérieur à. Au lieu d'évaluer l'expression à true ou false en Python, il la traduit dans la bonne syntaxe SQL pour la clause WHERE de la base de données. Maintenant, tu as un objet query complètement construit. Pour vraiment récupérer tes données, tu passes cet objet à une session. Tu peux le passer à la méthode execute standard sur la session, mais ça retourne un objet result où chaque ligne est essentiellement un tuple. Même si tu as requêté une seule classe ORM, ton objet mappé est coincé à l'intérieur d'un tuple à un seul élément, et tu dois l'extraire manuellement au retour. Voici le point clé. Quand tu veux récupérer des objets ORM, utilise plutôt la méthode scalars sur la session. Tu passes ton objet select à session point scalars. La session exécute la query, déballe automatiquement ces tuples du result set sous-jacent, et te retourne un itérable de tes objets ORM complètement peuplés. Tu obtiens immédiatement une collection propre d'instances User, prêtes à être modifiées ou lues. Le construct select unifié signifie que les mêmes blocs de construction de query que tu utilises pour les objets ORM peuvent être exécutés directement sur des connexions Core bas niveau, te laissant avec exactement un seul modèle mental à maintenir sur toute ta couche de base de données. C'est tout pour cet épisode. Merci d'avoir écouté, et continue de développer !
7

Faire le lien : Relations et JOINs

3m 59s

Liez vos tables de manière transparente. Apprenez à configurer les relations, à utiliser back_populates, et à gérer automatiquement les JOINs SQL entre les modèles liés.

Télécharger
Salut, c'est Alex de DEV STORIES DOT EU. SQLAlchemy, épisode 7 sur 7. Les bases de données relationnelles existent parce que les données sont connectées, mais écrire manuellement des jointures complexes à chaque fois que tu as besoin d'un enregistrement lié, ça devient vite lourd. Tu te retrouves avec une logique de query répétitive et dispersée dans toute ton application. Connecting the Dots: Relationships and Joins résout ce problème en laissant l'object-relational mapper gérer les connexions pour toi. Au niveau de la base de données, les tables se connectent avec des foreign keys. Si un user a plusieurs adresses, la table Address a une colonne qui stocke le user ID. C'est une relation one-to-many classique. Mais quand tu écris du code Python, tu ne veux pas extraire un ID et écrire une deuxième query juste pour trouver ces adresses. Tu veux accéder à une property sur ton objet user et voir instantanément une liste d'objets Address. C'est exactement ce qu'apporte le construct relationship. Il fait le pont entre les foreign keys de la base de données et les attributs des objets Python. Pour mettre ça en place, tu as besoin de deux choses. Premièrement, tu déclares la foreign key côté base de données. Dans ton modèle Address, tu définis une colonne appelée user ID et tu la marques explicitement comme une foreign key qui pointe vers la table User. Deuxièmement, tu définis la relationship côté Python. Dans ton modèle User, tu définis une property appelée addresses, en utilisant la fonction relationship, qui pointe vers le modèle Address. Tu définis aussi une property relationship sur le modèle Address qui pointe en retour vers le User. Voici le point clé. SQLAlchemy a besoin de savoir que ces deux properties représentent les deux côtés de la même connexion. Tu lui indiques ça en utilisant le paramètre back populates sur les deux définitions de relationship. Sur le modèle User, la relationship addresses définit back populates avec le nom en string de la property user. Sur le modèle Address, la relationship user définit back populates avec le nom en string de la property addresses. Grâce à back populates, l'ORM garde tes objets Python synchronisés en mémoire. Si tu prends un objet Address et que tu l'assignes à un user, cette adresse apparaît instantanément dans la liste d'adresses du user. Le framework gère le suivi avant même que tu ne fasses un commit dans la base de données. Une fois que tes objets sont liés, tu les query. Supposons que tu exécutes un statement select pour trouver un user nommé Alice. La base de données renvoie la row du user, et tu obtiens un objet. À ce moment précis, les adresses d'Alice ne sont pas chargées. L'ORM ne les fetch pas parce que tu ne les as pas encore demandées. Quand tu accèdes enfin à la property addresses dans ton code, peut-être pour itérer sur les noms de rue, l'ORM remarque que les données manquent. Il met automatiquement ton programme en pause, émet un deuxième statement select vers la base de données pour trouver toutes les adresses avec l'ID d'Alice, et popule la liste. C'est ce qu'on appelle le lazy loading. C'est le comportement par défaut parce que ça évite à l'application de pull des milliers de rows liées en mémoire à moins qu'elles ne soient strictement nécessaires. Tu query un user, tu accèdes à ses properties, et le système navigue de façon transparente à travers les foreign keys et lance les queries nécessaires en background. La vraie puissance du construct relationship, c'est qu'il cache la complexité mécanique des jointures, en te laissant naviguer dans une base de données entière juste en interagissant avec des objets Python connectés. Pour maîtriser ces concepts, explore la documentation officielle et essaie de configurer tes propres modèles hands-on. N'hésite pas à visiter devstories dot eu pour suggérer des sujets que tu veux voir abordés dans les prochaines séries. C'est tout pour cet épisode. Merci de ton écoute, et continue de build !