Torna al catalogo
Season 37 7 Episodi 25 min 2026

SQLAlchemy

v2.0 — Edizione 2026. Un corso audio completo su SQLAlchemy, che copre sia il Core che l'ORM, progettato per la versione 2.0 rilasciata nel 2026. Impara a mappare il tuo dominio, strutturare la tua applicazione, gestire le transazioni con la Session ed eseguire query in modo efficace.

Database ORM Python Core
SQLAlchemy
In Riproduzione
Click play to start
0:00
0:00
1
Le fondamenta: Cos'è SQLAlchemy e l'ORM?
Benvenuti in SQLAlchemy. Introduciamo l'architettura di base, spiegando la differenza tra il Core incentrato sullo schema e l'ORM incentrato sul dominio. Imparerai il gergo fondamentale e perché hai bisogno di un ORM.
3m 57s
2
L'Engine: La tua porta d'accesso al database
Ogni applicazione SQLAlchemy inizia con l'Engine. Scopri come stabilire la connettività, cos'è il connection pooling e come i dialect e le DBAPI colmano il divario con il tuo database.
3m 41s
3
Mappare il dominio: Declarative Base e modelli
Traduci automaticamente le tue classi Python in tabelle del database. Trattiamo DeclarativeBase, i tipi Mapped e come mapped_column costruisce i metadati del tuo database.
4m 00s
4
Layout del progetto: Strutturare la tua applicazione
L'organizzazione del codice è importante. Impara le migliori pratiche per strutturare il repository di un progetto SQLAlchemy in modo che engine, modelli e session rimangano puliti e manutenibili.
3m 27s
5
La Session: Padroneggiare la Unit of Work
Scopri il pattern Unit of Work attraverso la Session dell'ORM. Impara come aggiungere oggetti, quando si verificano i flush e come eseguire il commit delle transazioni in modo perfetto.
3m 28s
6
Interrogare i dati: Il costrutto moderno Select
Recupera i tuoi dati esattamente come ti servono. Esploriamo il costrutto unificato select() di SQLAlchemy 2.0, il filtraggio con where() e l'esecuzione delle query con la session.
3m 23s
7
Unire i puntini: Relazioni e JOIN
Collega le tue tabelle senza problemi. Scopri come configurare le relazioni, usare back_populates e gestire automaticamente le JOIN SQL tra modelli correlati.
3m 55s

Episodi

1

Le fondamenta: Cos'è SQLAlchemy e l'ORM?

3m 57s

Benvenuti in SQLAlchemy. Introduciamo l'architettura di base, spiegando la differenza tra il Core incentrato sullo schema e l'ORM incentrato sul dominio. Imparerai il gergo fondamentale e perché hai bisogno di un ORM.

Download
Ciao, sono Alex di DEV STORIES DOT EU. SQLAlchemy, episodio 1 di 7. Ti è mai capitato di droppare accidentalmente una tabella o di rovinare una query a causa di un errore di battitura in una stringa raw SQL? Le query create tramite concatenazione di stringhe sono fragili, difficili da mantenere e rappresentano un rischio per la sicurezza. Questo episodio parla della soluzione. Le basi: cosa sono SQLAlchemy e l'ORM? Il problema principale che gli sviluppatori affrontano quando scrivono applicazioni basate su database è che Python e i database relazionali ragionano in modo diverso. Python utilizza oggetti. Gli oggetti hanno stato, comportamento e relazioni complesse. I database relazionali utilizzano tabelle, colonne e righe. Si basano su primary e foreign key. Inserire un oggetto Python in una tabella del database richiede la traduzione del suo stato in uno statement SQL. Recuperarlo richiede il parsing delle righe e il ripopolamento di un nuovo oggetto. Questo attrito è chiamato object-relational impedance mismatch. Scrivere stringhe raw SQL per gestire questa traduzione è noioso e soggetto a errori. SQLAlchemy è un toolkit SQL per Python progettato per risolvere proprio questo problema. A differenza di alcuni tool che cercano di nascondere il database dietro un muro di astrazione, SQLAlchemy abbraccia SQL. Offre un workflow robusto e pythonico che genera SQL ottimale, lasciandoti il controllo. L'architettura è suddivisa in due parti distinte: il Core e l'ORM. Il Core di SQLAlchemy è la base. È command-oriented e schema-oriented. Questo significa che lavori direttamente con tabelle, colonne e righe del database, ma lo fai usando oggetti Python invece di stringhe di testo raw. Se vuoi selezionare dei dati, chiami un metodo select. Se vuoi filtrare, aggiungi in chain un metodo where. Questo elimina il rischio di errori di sintassi dovuti alla concatenazione di stringhe e ti protegge dalle SQL injection. Il Core è essenzialmente un SQL expression language. L'ORM, o Object Relational Mapper, si posiziona sopra il Core. Mentre il Core è schema-oriented, l'ORM è state e domain-oriented. Ecco il punto chiave. Con l'ORM, smetti di pensare a tabelle e righe, e inizi a pensare al domain model della tua applicazione. Definisci una classe Python standard, ad esempio una classe utente. Configuri l'ORM per mappare questa classe a una tabella utenti nel database, e mappi gli attributi della classe alle colonne della tabella. Quando fai una query sul database usando l'ORM, non ottieni indietro righe di dati raw. Ottieni indietro istanze completamente popolate della tua classe utente. L'ORM gestisce la traduzione in modo trasparente. Ancora più importante, tiene traccia dello stato di questi oggetti. Se modifichi un nome utente in Python, l'ORM si accorge che l'oggetto è stato modificato. Quando dici alla sessione del database di salvare le tue modifiche, l'ORM individua automaticamente i giusti statement di update e li esegue tramite il Core. Tu ti concentri sui tuoi oggetti Python e l'ORM gestisce la sincronizzazione col database. La cosa più importante da ricordare è che l'ORM non è una black box che sostituisce il Core. È un layer opzionale costruito interamente con componenti del Core, che ti permette di passare senza problemi alla generazione esplicita di SQL ogni volta che la tua domain logic richiede le massime performance. Se trovi questi episodi utili e vuoi supportare il podcast, puoi cercare DevStoriesEU su Patreon. Grazie per aver passato qualche minuto con me. Alla prossima, stammi bene.
2

L'Engine: La tua porta d'accesso al database

3m 41s

Ogni applicazione SQLAlchemy inizia con l'Engine. Scopri come stabilire la connettività, cos'è il connection pooling e come i dialect e le DBAPI colmano il divario con il tuo database.

Download
Ciao, sono Alex di DEV STORIES DOT EU. SQLAlchemy, episodio 2 di 7. Vuoi parlare con un database, ma database diversi parlano dialetti diversi, e i driver Python hanno tutti le loro peculiarità. Hai bisogno di un componente unico e unificato che gestisca il connection pooling, la traduzione dei dialetti e la gestione dei driver, senza far trapelare questi dettagli nella logica della tua applicazione. Questo componente è l'Engine. L'Engine è il punto di partenza per qualsiasi applicazione SQLAlchemy. Funge da registro centrale e da factory per le connessioni al database. Quando la tua applicazione deve parlare con il database, alla fine passa sempre attraverso l'Engine. Dietro le quinte, l'Engine mantiene un connection pool. Questo significa che mantiene una cache di connessioni attive al database, tenendole aperte e pronte all'uso. Riutilizzare le connessioni da un pool è molto più veloce che negoziare una nuova connessione di rete ogni singola volta che devi eseguire una query. Per istanziare un Engine, usi una funzione chiamata create engine. Questa funzione richiede un database URL. L'URL è una stringa che fornisce la posizione del database, le credenziali e due elementi di configurazione fondamentali: il dialetto e la DBAPI. Il dialetto è la famiglia di database a cui punti. SQL è uno standard, ma ogni database engine lo implementa in modo leggermente diverso. Il dialetto dice a SQLAlchemy se sta formattando le query per PostgreSQL, MySQL, Oracle o SQLite. Gestisce tutte le variazioni specifiche del vendor, così il tuo codice Python può rimanere coerente. Ecco il punto chiave. SQLAlchemy non si connette direttamente al tuo database. Delega sempre l'effettiva comunicazione di rete a un driver Python di terze parti. Questo driver è conosciuto come DBAPI. Se usi PostgreSQL, la tua DBAPI potrebbe essere psycopg2. Se usi SQLite, in genere è pysqlite. L'URL del database li specifica entrambi insieme. Per esempio, l'URL potrebbe iniziare con la stringa sqlite più pysqlite. Questo dice esplicitamente all'Engine di formattare le query usando il dialetto SQLite e di trasmetterle usando il driver pysqlite. Diamo un'occhiata a come configurare un database SQLite in-memory. Questo è un pattern molto comune per il testing, perché il database vive interamente nella RAM e scompare quando il programma termina. Chiami la funzione create engine e le passi la tua stringa URL. Per un database in-memory, l'URL è sqlite più pysqlite due punti slash slash slash due punti memory due punti. Quando esegui quella riga di codice, l'oggetto Engine viene creato, ma non si connette immediatamente al database. L'Engine è lazy. Prepara la configurazione e imposta il connection pool, ma aspetta fino alla primissima volta in cui gli chiedi esplicitamente di eseguire un task, prima di contattare effettivamente la DBAPI per stabilire una connessione. Durante lo sviluppo, hai spesso bisogno di verificare esattamente cosa SQLAlchemy sta inviando al database. La funzione create engine accetta un parametro opzionale chiamato echo. Se imposti echo su true, l'Engine farà il log di tutti gli statement SQL raw che genera, direttamente sul tuo standard output. Agisce come un tool di debugging integrato, permettendoti di vedere l'esatta traduzione tra le tue operazioni Python e l'SQL risultante. L'Engine esiste per astrarre la caotica realtà delle connessioni di rete e dei driver del database. Dà alla tua applicazione un'interfaccia pulita e stabile, assicurando che il resto del tuo codice non debba mai preoccuparsi di come il database venga raggiunto fisicamente. Grazie per averci fatto compagnia. Spero tu abbia imparato qualcosa di nuovo.
3

Mappare il dominio: Declarative Base e modelli

4m 00s

Traduci automaticamente le tue classi Python in tabelle del database. Trattiamo DeclarativeBase, i tipi Mapped e come mapped_column costruisce i metadati del tuo database.

Download
Ciao, sono Alex di DEV STORIES DOT EU. SQLAlchemy, episodio 3 di 7. Scrivi i tuoi domain model in Python, e poi devi scrivere un mucchio di codice SQL a parte solo per creare le tabelle che li conterranno. Col tempo, queste due definizioni separate finiscono quasi sempre per perdere la sincronizzazione. E se le tue classi Python potessero progettare automaticamente lo schema del tuo database? È esattamente ciò che risolviamo oggi con Mapping the Domain: Declarative Base and Models. Prima di esaminare le classi stesse, dobbiamo parlare dei metadata del database. In SQLAlchemy, i metadata sono di fatto un catalogo strutturale. Si tratta di un registro Python centrale che memorizza il blueprint esatto del tuo database. Tiene traccia di ogni tabella, ogni colonna e ogni constraint che definisci. Ogni volta che mappi una classe in SQLAlchemy, i dettagli di quella classe confluiscono direttamente in questo singolo catalogo. Per connettere le tue classi Python a questo catalogo di metadata, usi un costrutto chiamato DeclarativeBase. Non usi DeclarativeBase direttamente. Invece, crei la tua base class custom ereditando da esso. Da quel momento in poi, ogni singolo model che costruisci nella tua applicazione erediterà dalla tua base class custom. Nel momento in cui una classe eredita da essa, SQLAlchemy registra silenziosamente quel nuovo model nel catalogo di metadata sottostante. Considera come crei un model User. Definisci una classe Python chiamata User e la fai ereditare dalla tua base class custom. La prima cosa che definisci all'interno di questa classe è un attributo chiamato tablename, scritto con due underscore su entrambi i lati. Lo imposti sulla string user_account. Questa assegnazione esplicita indica a SQLAlchemy esattamente quale tabella del database sottostante memorizzerà questi oggetti Python. Successivamente, definisci le colonne. SQLAlchemy 2.0 si basa sui type hint standard di Python per fare ciò. Definisci i tuoi attributi usando un'annotazione speciale chiamata Mapped. Per un identificatore, annoti il tuo attributo ID come Mapped contenente un integer. Per un nome utente, lo annoti come Mapped contenente una string. Questo type hint è strettamente per Python. Indica al tuo IDE e al tuo type checker quali dati aspettarsi. Ma il database engine ha bisogno di istruzioni più specifiche rispetto a un semplice tipo Python. È qui che entra in gioco una funzione chiamata mapped_column. Assegni mapped_column all'attributo della tua classe e, tra parentesi, configuri le regole specifiche del database. Per il tuo user ID, chiami mapped_column e passi un flag che lo contrassegna esplicitamente come primary key. Per una colonna string, potresti passare un limite massimo di caratteri o un flag che richiede che il campo sia unique. Se il tuo type hint Python fornisce tutto il contesto di cui SQLAlchemy ha bisogno, puoi in realtà omettere completamente mapped_column. SQLAlchemy dedurrà una colonna di database standard e di base direttamente dall'annotazione Mapped. Ma per le primary key, o per qualsiasi constraint specifico del database, mapped_column è strettamente richiesto. Ecco il punto chiave. Una volta scritte le tue classi Python, la progettazione del tuo schema è già finita. Il catalogo dei metadata ora contiene una mappa perfetta del tuo dominio. Puoi chiamare un metodo chiamato create_all sui metadata della tua base class, passando il tuo database engine. SQLAlchemy analizza la tua classe User, legge le colonne mappate, le traduce all'istante in statement CREATE TABLE formattati perfettamente e le applica. Lo stesso identico codice Python che scrivi per eseguire la tua applicazione diventa la single source of truth che costruisce la struttura del tuo database. Grazie per avermi fatto compagnia. Spero tu abbia imparato qualcosa di nuovo.
4

Layout del progetto: Strutturare la tua applicazione

3m 27s

L'organizzazione del codice è importante. Impara le migliori pratiche per strutturare il repository di un progetto SQLAlchemy in modo che engine, modelli e session rimangano puliti e manutenibili.

Download
Ciao, sono Alex di DEV STORIES DOT EU. SQLAlchemy, episodio 4 di 7. Hai scritto i tuoi models, il tuo engine e le tue query. Ma buttarli tutti in un singolo file è la strada più veloce verso un incubo di manutenzione e circular import. La soluzione è il Project Layout: strutturare la tua applicazione. Quando guardi i tutorial, di solito il codice è contenuto in un unico script. Vedi il setup dell'engine, la base class, i models e le query impilati dall'alto verso il basso. Questo design è ottimo per imparare la sintassi, ma se provi a costruire un sistema in produzione in questo modo, crolla tutto. Non appena aggiungi delle web route o dei background worker, finisci nei guai. Se una web route deve importare uno user model, ma lo user model si trova nello stesso file che avvia l'application server o inizializza la connessione al database, Python va in confusione. Ti ritrovi con dei circular import e il tuo codice diventa impossibile da testare. Hai bisogno di una rigorosa separation of concerns. Il pattern standard è suddividere quel singolo script in moduli distinti in base al loro compito. Per prima cosa, crei un file dedicato, di solito chiamato database dot py. Questo file ha esattamente un'unica responsabilità, ovvero gestire la connessione al database. È qui che metti la tua chiamata a create engine e configuri il tuo session maker. Qui non definisci le tabelle e non esegui le query. Isolando l'engine, ti assicuri che la tua applicazione crei un solo connection pool. Qualsiasi altro modulo nel tuo progetto può importare in sicurezza il session maker da questo file senza innescare accidentalmente la logica di startup dell'applicazione. Successivamente, sposti le definizioni delle tue tabelle in un file chiamato models dot py. Questo file contiene il tuo Declarative Base e tutte le tue mapped class, come un oggetto User o Address. Non importa nulla dal tuo file database. Questa è la parte che conta. I tuoi models definiscono la forma dei tuoi dati, ma non sanno come connettersi al database. Dato che models dot py non ha dipendenze dall'engine o dalla session attiva, puoi importare le tue mapped class ovunque nella tua applicazione senza preoccuparti dei side effect. Se il tuo progetto diventa grande, potresti persino dividere i models in una directory con file separati per domini diversi. Finché tutti importano l'esatta stessa istanza di Declarative Base, SQLAlchemy sa che appartengono alla stessa metadata collection. Infine, ti serve un posto dove eseguire effettivamente le tue query. Tieni l'esecuzione delle query completamente separata dai tuoi models e dai file di connessione. Di solito, questo va nei tuoi route handler o in dei service file dedicati. Quando un'applicazione ha bisogno di dati, il service file importa il session maker dal tuo modulo database, e le mapped class necessarie dal tuo modulo models. Apre una session usando un context manager, esegue uno statement select, recupera gli oggetti e lascia che il context manager chiuda in modo sicuro la connessione quando il blocco finisce. Il service file fa da coordinatore. Strutturare il tuo progetto in questo modo mantiene la tua logica di connessione isolata, i tuoi data schema portabili e le tue operazioni di business pulite. La singola regola più importante da ricordare è che i tuoi data model non dovrebbero mai importare il tuo database engine. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
5

La Session: Padroneggiare la Unit of Work

3m 28s

Scopri il pattern Unit of Work attraverso la Session dell'ORM. Impara come aggiungere oggetti, quando si verificano i flush e come eseguire il commit delle transazioni in modo perfetto.

Download
Ciao, sono Alex di DEV STORIES DOT EU. SQLAlchemy, episodio 5 di 7. Immagina una staging area in cui puoi mettere in coda decine di modifiche al database e pusharle tutte perfettamente in una volta, oppure cancellarle all'istante. Questo è esattamente il problema risolto dal concetto presentato in questo episodio: la Session: padroneggiare la Unit of Work. La Session di SQLAlchemy è il modo principale in cui i tuoi oggetti Python comunicano con il database. Funziona secondo un pattern chiamato Unit of Work. Quando usi la Session, non stai inviando singoli comandi direttamente al database. Stai interagendo con uno smart workspace. La Session monitora le tue operazioni, raccoglie aggiunte, modifiche ed eliminazioni e calcola il modo più efficiente per tradurle in SQL al momento opportuno. All'interno di questo workspace si trova un meccanismo chiamato Identity Map. Si tratta di un dizionario interno che collega la primary key di una riga del database allo specifico indirizzo di memoria del tuo oggetto Python. Se richiedi un utente con ID 5, la Session controlla prima la Identity Map. Se l'oggetto è già caricato, ottieni indietro esattamente la stessa istanza Python. Questo ti garantisce di non avere mai due oggetti Python distinti che rappresentano la stessa riga del database contemporaneamente. Vediamo come inserire nuovi dati. Inizi creando un'istanza della tua classe User mappata, assegnandole un nome come Alice. In questa fase, il tuo oggetto si trova in uno stato chiamato transient. Esiste esclusivamente nella memoria Python. Il database non sa nulla della sua esistenza e la Session non ne è a conoscenza. Per collegare l'oggetto al tuo workspace, lo passi al metodo add della Session. L'oggetto passa a uno stato chiamato pending. La Session ha registrato la tua intenzione di inserire questo nuovo utente, ma non ha ancora inviato alcun SQL sulla rete. L'oggetto è semplicemente in attesa nella coda. Questo introduce la differenza fondamentale tra un flush e un commit. Quando chiami flush sulla Session, questa prende la tua coda pending e pusha le modifiche nella transazione corrente del database. Emette l'istruzione SQL INSERT. Il database la esegue e assegna una primary key. Tornando al tuo codice Python, il tuo oggetto utente viene immediatamente popolato con quel nuovo ID del database. L'oggetto è passato a uno stato persistent. Ecco il punto chiave. Anche se l'oggetto è persistent e ha un ID, la modifica non è ancora permanente. Il database sta isolando questa transazione. Supponiamo che il tuo codice rilevi improvvisamente un errore, come un indirizzo email non valido per l'utente su cui hai appena chiamato flush. Poiché non hai ancora fatto commit, puoi chiamare un rollback. Il database scarta completamente la transazione e nulla viene salvato nelle tabelle effettive. Quando sei assolutamente certo che i dati siano corretti, chiami commit. Un commit finalizza la transazione e salva i dati su disco. Chiamare commit attiva automaticamente prima un flush, quindi in genere non è necessario chiamare flush manualmente a meno che tu non debba specificamente leggere una primary key generata prima di completare il resto della tua logica. La Session funge da buffer tra la memoria della tua applicazione e lo storage permanente, consentendoti di orchestrare modifiche complesse ai dati in modo sicuro prima di bloccarle definitivamente nella realtà. Grazie per averci ascoltato. Stammi bene.
6

Interrogare i dati: Il costrutto moderno Select

3m 23s

Recupera i tuoi dati esattamente come ti servono. Esploriamo il costrutto unificato select() di SQLAlchemy 2.0, il filtraggio con where() e l'esecuzione delle query con la session.

Download
Ciao, sono Alex di DEV STORIES DOT EU. SQLAlchemy, episodio 6 di 7. Nelle versioni precedenti di SQLAlchemy, fare query era come lavorare con un cervello diviso. Dovevi memorizzare una sintassi per le operazioni Core e un set di regole completamente diverso per l'ORM. Ogni volta che scrivevi una query, dovevi fermarti e chiederti se stavi recuperando righe raw o oggetti mappati. La versione 2.0 ha eliminato questa linea di demarcazione con il moderno costrutto Select. Il concetto è semplice. Ora c'è una singola funzione unificata chiamata select. Le passi direttamente l'entità su cui vuoi fare la query. Se hai una classe ORM chiamata User, passi la classe User alla funzione select. Questo crea un oggetto select che rappresenta una query sulla tabella user sottostante. Questo oggetto select è generativo. Chiamare la funzione select non esegue nulla sul database. Invece, crea un expression object in memoria. Quando chiami un metodo su questo oggetto per aggiungere delle condizioni, ti restituisce un nuovo oggetto select con quelle nuove condizioni applicate. Questo ti permette di costruire query complesse dinamicamente, passo dopo passo, passando l'oggetto in giro per la tua applicazione prima ancora di parlare con il database. Questo copre la query di base. Come la filtri? Aggiungi il metodo where al tuo oggetto select. All'interno del metodo where, usi gli operatori Python standard direttamente sugli attributi della tua classe mappata. Per esempio, per trovare un utente specifico, scrivi la classe User punto name, seguito da due segni di uguale e dalla stringa target. SQLAlchemy intercetta tutto questo. Fa l'overload degli operatori Python standard, come il doppio uguale o il segno di maggiore. Invece di valutare l'espressione come true o false in Python, la traduce nella sintassi SQL corretta per la clausola WHERE del database. Ora hai un query object completamente costruito. Per recuperare effettivamente i tuoi dati, passi questo oggetto a una session. Puoi passarlo al metodo execute standard sulla session, ma questo restituisce un result object dove ogni riga è essenzialmente una tuple. Anche se hai fatto una query su una singola classe ORM, il tuo oggetto mappato è intrappolato dentro una tuple di un solo elemento, e devi estrarlo manualmente quando viene restituito. Ecco il punto chiave. Quando vuoi indietro degli oggetti ORM, usa invece il metodo scalars sulla session. Passi il tuo oggetto select a session punto scalars. La session esegue la query, fa automaticamente l'unwrap di quelle tuple dal result set sottostante, e genera un iterable dei tuoi oggetti ORM completamente popolati. Ottieni immediatamente una collection pulita di istanze User, pronte da modificare o leggere. Il costrutto select unificato significa che gli stessi identici building block della query che usi per gli oggetti ORM possono essere eseguiti direttamente su connessioni Core di basso livello, lasciandoti con un solo modello mentale da mantenere per l'intero database layer. Questo è tutto per questo episodio. Grazie per l'ascolto, e continuate a sviluppare!
7

Unire i puntini: Relazioni e JOIN

3m 55s

Collega le tue tabelle senza problemi. Scopri come configurare le relazioni, usare back_populates e gestire automaticamente le JOIN SQL tra modelli correlati.

Download
Ciao, sono Alex di DEV STORIES DOT EU. SQLAlchemy, episodio 7 di 7. I database relazionali esistono perché i dati sono collegati, ma scrivere join complesse a mano ogni volta che ti serve un record correlato diventa presto noioso. Ti ritrovi con una logica di query ripetitiva sparsa per tutta l'applicazione. Connecting the Dots: Relationships and Joins risolve questo problema lasciando che l'object-relational mapper gestisca le connessioni al posto tuo. A livello di database, le tabelle si collegano tramite foreign key. Se un utente ha più indirizzi, la tabella degli indirizzi ha una colonna che memorizza l'user ID. Questa è una standard relationship uno-a-molti. Ma quando scrivi codice Python, non vuoi estrarre un ID e scrivere una seconda query solo per trovare quegli indirizzi. Vuoi accedere a una property sul tuo oggetto user e vedere immediatamente una lista di oggetti address. Questo è esattamente ciò che offre il costrutto relationship. Colma il divario tra le foreign key del database e gli attributi degli oggetti Python. Per configurarlo, ti servono due cose. Primo, dichiari la foreign key lato database. Nel tuo modello Address, definisci una colonna chiamata user ID e la marchi esplicitamente come foreign key che punta alla tabella User. Secondo, definisci la relationship lato Python. Nel tuo modello User, definisci una property chiamata addresses, usando la funzione relationship, che punta al modello Address. Definisci anche una property relationship sul modello Address che punta di nuovo a User. Ecco il punto chiave. SQLAlchemy deve sapere che queste due property rappresentano i due lati della stessa identica connessione. Glielo comunichi usando il parametro back populates in entrambe le definizioni della relationship. Sul modello User, la relationship addresses imposta back populates al nome in formato string della property user. Sul modello Address, la relationship user imposta back populates al nome in formato string della property addresses. Grazie a back populates, l'ORM mantiene i tuoi oggetti Python sincronizzati in memoria. Se prendi un oggetto address e lo assegni a un user, quell'address appare immediatamente nella lista di address dell'user. Il framework gestisce tutto il bookkeeping prima ancora che tu faccia qualsiasi commit sul database. Una volta che i tuoi oggetti sono collegati, puoi fare delle query. Supponi di eseguire uno statement select per trovare un user di nome Alice. Il database restituisce la riga dell'user e tu ottieni un oggetto. In questo preciso istante, gli address di Alice non sono caricati. L'ORM non fa il fetch perché non li hai ancora richiesti. Quando finalmente accedi alla property addresses nel tuo codice, magari per iterare sui nomi delle vie, l'ORM nota che i dati mancano. Mette automaticamente in pausa il tuo programma, lancia un secondo statement select verso il database per trovare tutti gli address con l'ID di Alice, e popola la lista. Questo si chiama lazy loading. È il comportamento di default perché impedisce all'applicazione di caricare in memoria migliaia di righe correlate a meno che non siano strettamente necessarie. Fai una query su un user, accedi alle sue property, e il sistema naviga in modo trasparente tra le foreign key e lancia le query necessarie in background. Il vero potere del costrutto relationship è che nasconde la complessità meccanica delle join, permettendoti di navigare in un intero database semplicemente interagendo con oggetti Python collegati. Per padroneggiare questi concetti, esplora la documentazione ufficiale e prova a configurare i tuoi modelli hands-on. Sentiti libero di visitare devstories dot eu per suggerire argomenti che vorresti fossero trattati nelle serie future. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!