Torna al catalogo
Season 38 8 Episodi 30 min 2026

Alembic Database Migrations

v1.18 — Edizione 2026. Padroneggia le migrazioni di database con Alembic 1.18 in Python. Scopri come gestire le modifiche allo schema, utilizzare autogenerate, gestire i vincoli, scrivere script offline e orchestrare efficacemente le migrazioni di database insieme a SQLAlchemy.

Database Migrazioni di Database ORM
Alembic Database Migrations
In Riproduzione
Click play to start
0:00
0:00
1
L'importanza delle migrazioni
Scopri perché la gestione manuale degli schemi fallisce su larga scala e come Alembic porta il controllo di versione nel tuo database relazionale. Esploriamo il modello mentale alla base delle migrazioni di database e analizziamo l'anatomia dell'ambiente Alembic.
3m 18s
2
Anatomia di una revisione
Ripercorri il ciclo di vita della tua primissima migrazione con Alembic. Analizziamo le funzioni upgrade() e downgrade() e riveliamo come funziona realmente il tracciamento delle versioni all'interno del database.
3m 51s
3
La magia e i limiti di autogenerate
Scopri come Alembic rileva automaticamente le modifiche confrontando i tuoi modelli SQLAlchemy con i metadati del database in tempo reale. Impara cosa riesce a catturare perfettamente e cosa invece gli sfugge.
3m 28s
4
L'importanza di nominare i vincoli
Scopri perché affidarsi ai nomi generati dal database per i vincoli è la ricetta per un disastro nelle migrazioni. Impara a configurare una convenzione di denominazione unificata per il tuo sistema.
3m 45s
5
Migrazioni offline e generazione di SQL
Esplora come generare script SQL puri per gli amministratori del tuo database invece di eseguire Python direttamente sul database di produzione. Discutiamo il flusso di esecuzione offline.
4m 38s
6
Migrazioni batch per SQLite
Affronta la sfida di modificare le tabelle in SQLite, che non ha un supporto completo per ALTER TABLE. Impara il flusso di lavoro 'move and copy' utilizzando le operazioni batch di Alembic.
3m 48s
7
Lavorare con i branch
Padroneggia la collaborazione in team gestendo flussi di migrazione ramificati. Scopri come identificare e unire cronologie di revisione divergenti quando più sviluppatori modificano il database.
3m 19s
8
Power-Up per la produzione
Migliora la tua conoscenza di Alembic con tecniche avanzate. Trattiamo l'invocazione programmatica dei comandi e la condivisione di una connessione con framework applicativi come FastAPI.
4m 11s

Episodi

1

L'importanza delle migrazioni

3m 18s

Scopri perché la gestione manuale degli schemi fallisce su larga scala e come Alembic porta il controllo di versione nel tuo database relazionale. Esploriamo il modello mentale alla base delle migrazioni di database e analizziamo l'anatomia dell'ambiente Alembic.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Migrazioni di database con Alembic, episodio 1 di 8. Esegui un comando manuale sul database direttamente sul tuo server di produzione per aggiungere una singola colonna, e improvvisamente l'applicazione si blocca. Il codice che hai deployato si aspettava una struttura, il database ora ne ha un'altra, e non c'è un semplice pulsante di undo. Ecco perché oggi parliamo dell'importanza delle migrazioni. Quando un progetto software inizia, modificare il database schema è facile. Fai il drop delle tabelle e le ricrei. Una volta che hai utenti reali e dati reali, questa non è più un'opzione. Fare modifiche manuali in ambienti diversi come development, staging e produzione porta inevitabilmente a un mismatch. Il codice della tua applicazione si basa su uno stato specifico del database. Quando questo stato va in drift, l'applicazione fallisce. Alembic risolve questo problema facendo da version control per il tuo database schema. Proprio come tieni traccia della history del tuo source code, Alembic tiene traccia della history della struttura del tuo database. Per usare Alembic, devi inizializzare un migration environment. Si tratta di una struttura di directory dedicata che committi nel tuo repository di source control insieme al codice della tua applicazione. Contiene le istruzioni e la configurazione necessarie per modificare il tuo database nel tempo. L'environment è composto da tre elementi principali. Il primo è il file di configurazione root, chiamato alembic punto ini. Questo file si trova alla base del tuo progetto. Salva le impostazioni di base, principalmente la connection URL del database, dicendo ad Alembic dove si trova effettivamente il target database. Il secondo elemento è la directory versions. Qui è dove vengono salvati i migration script. Ogni volta che devi modificare il database schema, viene creato un nuovo script Python in questa cartella. Ogni script definisce due azioni: una funzione di upgrade per applicare la modifica, e una funzione di downgrade per annullarla. Se devi aggiungere una tabella per i profili utente, l'istruzione esatta per quella modifica si trova in uno script proprio qui. L'ultimo elemento è un file chiamato env punto py. È facile confonderlo con un file di configurazione generale dell'applicazione o con un posto dove salvare le variabili di sistema, ma non è questo il suo scopo. Ecco il punto chiave. Il file env punto py fa specificamente da ponte tra i model della tua applicazione e il migration engine di Alembic. Configura il database engine, gestisce il lifecycle della connessione e, cosa più importante, carica i tuoi metadata di SQLAlchemy. Questo dice ad Alembic esattamente come sono fatti i tuoi model nel codice, così sa a quale database schema dovrà alla fine corrispondere. Ogni volta che invochi un comando Alembic, questo esegue prima lo script env punto py per stabilire il context di cui ha bisogno per operare. Invece di affidarti alla fragile memoria dei comandi manuali sul database, hai un processo strutturato e ripetibile. Il vero valore dell'environment di Alembic non è solo che esegue i comandi in sicurezza, ma che crea una history definitiva e versionata di come le tue strutture dati si sono evolute fin dal primo giorno. Se ti piace il podcast e vuoi supportare lo show, puoi trovarci cercando DevStoriesEU su Patreon. Vorrei prendermi un momento per ringraziarti di averci ascoltato: ci aiuta tantissimo. Buona giornata!
2

Anatomia di una revisione

3m 51s

Ripercorri il ciclo di vita della tua primissima migrazione con Alembic. Analizziamo le funzioni upgrade() e downgrade() e riveliamo come funziona realmente il tracciamento delle versioni all'interno del database.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Migrazioni di database con Alembic, episodio 2 di 8. Ti sei mai chiesto come un database sappia effettivamente quale versione dello schema sta eseguendo? Non ispeziona le tue tabelle né tira a indovinare in base a quali colonne esistono. Il segreto sta in una singola tabella nascosta, e capire come si collega al tuo codice è il fulcro dell'Anatomia di una Revisione. Per modificare lo schema di un database, per prima cosa crei uno script di migrazione. Lo fai eseguendo il comando revision di Alembic insieme a un breve messaggio descrittivo, come create account table. Alembic genera un nuovo file Python nella tua directory versions. Il nome del file inizia con una string casuale di caratteri, seguita dal tuo messaggio. Quella string è un GUID parziale, un globally unique identifier. Alembic usa questi identificatori invece di interi sequenziali per evitare merge conflict quando più developer creano migrazioni su branch diversi allo stesso tempo. Se apri quel nuovo file Python, vedrai due variabili in alto: revision e down revision. La variabile revision contiene il GUID per questo specifico script. La variabile down revision contiene il GUID dello script che è venuto subito prima. Ecco il punto chiave. I developer spesso pensano che le migrazioni vengano applicate in ordine in base ai timestamp di creazione dei file o all'ordine alfabetico dei nomi dei file. Non è così. Alembic si basa rigorosamente sulla chain di down revision. Legge queste variabili all'interno dei file per costruire una linked list della history del tuo schema. Se uno script non punta a una revision precedente valida, la chain si rompe. Sotto queste variabili di routing, troverai due funzioni vuote: upgrade e downgrade. È qui che scrivi manualmente le modifiche al tuo schema. Nel nostro scenario, stiamo aggiungendo una tabella account. Nella funzione upgrade, scrivi la logica per creare la tabella, definendo le tue colonne, come una primary key integer e una string per il nome dell'account. La funzione downgrade deve fare esattamente l'opposto. Se upgrade crea la tabella account, downgrade deve fare il drop. Ogni passo in avanti deve avere un corrispondente e affidabile passo indietro. Una volta scritto il tuo script, lo applichi eseguendo il comando Alembic upgrade, puntandolo ad head, che significa la revision più recente nella tua chain. Ecco cosa succede dietro le quinte. Alembic si connette al tuo database e cerca una tabella chiamata alembic version. Se questa è la tua prima migrazione, la tabella non esiste ancora, quindi Alembic la crea. Questa tabella ha esattamente una riga e una colonna, che memorizza il GUID della revision attualmente applicata. Alembic controlla questa tabella, vede a che punto si trova attualmente il database, ed esegue le funzioni di upgrade di ogni script necessario per raggiungere la target revision. Infine, aggiorna la tabella version con il tuo nuovo GUID. Se testi la tua nuova feature account e ti accorgi che qualcosa non va, puoi fare un rollback in modo pulito. Esegui il comando Alembic downgrade, passando un identificatore relativo come meno uno per tornare indietro di una singola revision. Alembic guarda la version corrente nel database, trova lo script corrispondente, ed esegue la sua funzione di downgrade. Fa il drop della tabella account e aggiorna la tabella version con il GUID precedente. La cosa più importante da ricordare è che uno script di migrazione non è solo una raccolta sparsa di comandi del database. È un nodo autonomo in una linked list che dà al tuo database un percorso preciso per muoversi sia in avanti che indietro nel tempo. Grazie per aver passato qualche minuto con me. Alla prossima, stammi bene.
3

La magia e i limiti di autogenerate

3m 28s

Scopri come Alembic rileva automaticamente le modifiche confrontando i tuoi modelli SQLAlchemy con i metadati del database in tempo reale. Impara cosa riesce a catturare perfettamente e cosa invece gli sfugge.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Database Migrations con Alembic, episodio 3 di 8. Affidarti ciecamente a un tool automatizzato per scrivere le tue database migrations è il modo più rapido per cancellare accidentalmente le tue tabelle in produzione. Il problema sta nel capire cosa vede effettivamente il tool quando confronta il tuo codice con la realtà, ed è proprio di questo che parliamo oggi in "La magia e i limiti di autogenerate". Alembic ha una feature fondamentale chiamata autogenerate. Quando esegui un comando revision con il flag autogenerate, Alembic fa un confronto. Per prima cosa, si connette al tuo database live e ispeziona lo schema attuale. In secondo luogo, esamina il target state definito dai modelli SQLAlchemy nel codice della tua applicazione. Confronta questi due stati e individua le differenze. C'è un malinteso comune su questo step. Autogenerate non applica magicamente queste modifiche al tuo database. Scrive semplicemente una bozza di script Python che contiene le operazioni di migrazione che ritiene necessarie per far corrispondere il database ai tuoi modelli. Devi fare una review di questo script candidato prima di eseguirlo effettivamente sul tuo database. Quando confronta il tuo database con i tuoi modelli, autogenerate rileva in modo affidabile le modifiche strutturali di base. Se aggiungi una nuova classe model al tuo codice, Alembic prepara un'istruzione per creare una tabella. Se rimuovi una classe model, prepara un'istruzione per fare il drop di quella tabella. Rileva correttamente quando aggiungi o rimuovi colonne, quando modifichi una colonna per consentire valori null, o quando aggiungi indici di base e unique constraint. Per queste operazioni standard, la feature ti fa risparmiare un'enorme quantità di codice scritto a mano. Ecco la parte importante. Autogenerate ha dei punti ciechi perché non può leggerti nel pensiero. Supponiamo che tu decida di rinominare una tabella esistente nei tuoi modelli SQLAlchemy. Aggiorni il codice ed esegui il comando autogenerate, aspettandoti che Alembic prepari un comando sicuro per fare un alter del nome della tabella. Invece, propone qualcosa di estremamente pericoloso. Prepara un comando per fare il drop completo della vecchia tabella, distruggendo tutti i dati al suo interno, e poi prepara un secondo comando per creare una tabella nuova di zecca con il nuovo nome. Alembic fa questo perché vede solo che il vecchio nome della tabella esiste nel database ma manca nei tuoi modelli, e che un nuovo nome della tabella esiste nei tuoi modelli ma manca nel database. Non ha modo di collegare le due cose come un semplice rename. Devi modificare manualmente lo script generato per usare invece un'operazione di rename table. La stessa identica limitazione si applica al rename delle colonne. Autogenerate interpreterà una colonna rinominata come un'istruzione per fare il drop di quella vecchia e aggiungerne una nuova. Oltre ai rename, ci sono modifiche che autogenerate ignorerà completamente di default. Se cambi il data type di una colonna, o se modifichi un valore di server default, Alembic salterà quelle differenze. Puoi configurare il tool per rilevare le modifiche di type e default, ma devi attivare esplicitamente queste impostazioni nella tua environment configuration. Anche con queste impostazioni abilitate, non rileverà mai le modifiche ai sequence object o ai nomi dei constraint. Il modo più sicuro di trattare autogenerate è come un tool di dettatura ad alta velocità che gestisce il boilerplate al posto tuo, piuttosto che come un sistema intelligente che capisce l'intento dietro alle modifiche del tuo codice. Grazie per avermi fatto compagnia. Spero tu abbia imparato qualcosa di nuovo.
4

L'importanza di nominare i vincoli

3m 45s

Scopri perché affidarsi ai nomi generati dal database per i vincoli è la ricetta per un disastro nelle migrazioni. Impara a configurare una convenzione di denominazione unificata per il tuo sistema.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Migrazioni di database con Alembic, episodio 4 di 8. Il modo più semplice per rovinare un deploy è cercare di droppare un constraint del database a cui non hai mai dato un nome. Il tuo migration script funziona perfettamente sulla tua macchina locale, ma l'ambiente di staging è appena crashato, lamentandosi di una foreign key mancante con un nome imprevedibile come SYS C 0 0 2 9 3 3 4. La colpa è dell'essersi affidati a identificatori generati dal database. Questo episodio copre l'importanza di dare un nome ai constraint e come automatizzare il processo. Molti sviluppatori danno per scontato che l'Object-Relational Mapper gestisca il drop delle colonne e delle relative regole senza problemi. Definiscono un unique constraint o una foreign key nel loro model, omettono il nome per risparmiare tempo e vanno avanti. Quando fai così, il database engine prende il controllo. Sistemi come Postgres o Oracle auto-assegneranno un nome arbitrario generato dal sistema per applicare quella regola. Questo crea una bomba a orologeria per le migrazioni future. Quando alla fine devi fare un alter o un drop di quella tabella o colonna, Alembic usa l'operazione di drop constraint. Quell'operazione richiede rigorosamente il nome esatto del constraint di destinazione. Se lasci che il database generi il nome, sarà quasi certamente diverso in development rispetto a staging o produzione. Finisci per hardcodare il nome di un constraint locale nel tuo migration script, che fallirà immediatamente quando eseguito in un ambiente diverso in cui quella string random non esiste. Per risolvere questo problema, ogni singolo constraint nel tuo database deve avere un nome esplicito e deterministico. Farlo manualmente su centinaia di model è noioso ed è facile dimenticarsene. L'approccio migliore è configurare un dictionary di naming convention sul tuo oggetto MetaData di SQLAlchemy. Questo dictionary funge da template globale per la tua applicazione. Definisci le regole per ogni tipo di constraint. Ad esempio, puoi specificare che ogni index debba essere nominato usando il prefisso i x, seguito dal nome della tabella, seguito dal nome della colonna. Imposti template simili per unique constraint, check constraint e foreign key. Poi colleghi questo oggetto MetaData configurato alla tua declarative base class. Ecco la parte che conta. Una volta che questo dictionary è al suo posto, Alembic integra automaticamente le tue naming convention sia nella sua feature di autogenerate che nelle sue operazioni manuali. Quando esegui un comando per fare l'autogenerate di una nuova migrazione, Alembic guarda i tuoi model, vede un nuovo constraint e controlla il dictionary MetaData. Applica il tuo template, calcola il nome esplicito e scrive esattamente quella string nel python script generato. Dato che lo script generato comanda esplicitamente al database di usare quel nome specifico, il constraint sarà identico in ogni singolo ambiente. Questa integrazione si estende alle operazioni di Alembic eseguite durante il processo di upgrade stesso. Se un migration script include un'operazione di create table o add column con constraint inline privi di nomi espliciti, Alembic non li passa semplicemente alla cieca al database. Li intercetta, consulta il template di naming convention e assegna il nome deterministico corretto prima di eseguire i comandi sul database. Una naming convention deterministica garantisce che una regola creata sulla tua macchina locale condividerà l'esatto stesso identificatore quando raggiungerà i tuoi server di produzione, eliminando completamente il rischio di constraint non droppabili. Grazie per l'ascolto. Statemi bene, a tutti.
5

Migrazioni offline e generazione di SQL

4m 38s

Esplora come generare script SQL puri per gli amministratori del tuo database invece di eseguire Python direttamente sul database di produzione. Discutiamo il flusso di esecuzione offline.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Migrazioni di database con Alembic, episodio 5 di 8. Negli ambienti enterprise altamente regolamentati, gli sviluppatori non ottengono quasi mai l'accesso diretto per eseguire Python sui database in produzione. Quando arriva il giorno del deploy, i database administrator di solito ti bloccano l'accesso e richiedono invece uno script raw da poter revisionare. A colmare questo divario tra la tua codebase Python e il loro rigido processo di deploy ci pensano le migrazioni offline e la generazione di SQL. Normalmente, Alembic si connette a un database live ed esegue le modifiche allo schema direttamente tramite quella connessione. Ma quando devi consegnare un file di testo semplice a un team di DBA, usi la modalità offline. Aggiungendo il flag trattino trattino sql ai tuoi comandi di upgrade o downgrade nel terminale, Alembic cambia completamente il suo comportamento di esecuzione. Invece di eseguire gli statement contro un database engine, li renderizza come una string continua di SQL standard e li stampa direttamente sullo standard output. Puoi facilmente reindirizzare questo output del terminale in un file di testo. Questo duplice comportamento non è magia, è definito esplicitamente nel file environment del tuo progetto, in genere chiamato env punto py. Se guardi dentro quel file, troverai due funzioni di routing distinte. La prima è run migrations online. Questa funzione crea un database engine live, associa una connessione attiva al context di Alembic, ed esegue i tuoi script di migrazione passo dopo passo. La seconda funzione è run migrations offline, ed è qui che avviene la traduzione. Quando passi il flag sql, Alembic rileva il flag e lancia invece questa funzione offline. Configura il context usando solo un database URL. Non viene stabilita alcuna connessione di rete e non viene istanziato alcun engine. Poi prende le tue strutture di migrazione Python e genera gli statement CREATE, ALTER o DROP esatti, li racchiude in blocchi di transazione BEGIN e COMMIT standard, e formatta tutto per il tuo specifico dialetto database. Ecco il punto chiave. Poiché la modalità offline non si connette mai effettivamente al database, i tuoi script di migrazione non possono fare affidamento sullo stato attivo del database. Non puoi eseguire uno statement SELECT all'interno di una migrazione offline per verificare se una riga esiste, e non puoi ispezionare lo stato corrente di una tabella prima di fare una modifica. Se il tuo codice Python si aspetta che un cursore del database restituisca dei dati per decidere quale modifica allo schema fare, la generazione offline fallirà. Lo script deve essere puramente dichiarativo. Dice semplicemente ad Alembic quali strutture generare. Prendi uno sviluppatore che sta finendo un feature branch locale. Ha eseguito le migrazioni in locale in modalità online per verificare che tutto funzioni contro il suo database di test. Per la release in produzione, esegue il comando di upgrade con una start revision e una end revision specifiche, aggiunge il flag sql e reindirizza l'output su un file di testo. Il risultato è uno script SQL pulito e sequenziale. Lo sviluppatore consegna questo file al team di DBA. I DBA possono leggerlo, verificarlo secondo le loro rigide policy di sicurezza, e applicarlo durante la maintenance window usando i tool standard di amministrazione del database. Hai anche il controllo su come viene generato questo output offline. All'interno della funzione run migrations offline, la chiamata context configure accetta parametri che modificano l'SQL renderizzato. Un requisito comune è la conversione delle variabili in valori literal. Abilitando i literal binds nella configurazione, ti assicuri che qualsiasi dato inserito durante la migrazione includa i valori effettivi direttamente nella string SQL, anziché produrre in output dei marker di parametro generici. Questo assicura che l'output sia uno script completamente self-contained e pronto per l'esecuzione. Il vero valore della generazione offline è la prevedibilità; trasforma i cambi di stato dinamici di Python in SQL statico e auditabile, che qualsiasi pipeline di deploy o team di sicurezza può verificare prima che una singola tabella venga modificata. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
6

Migrazioni batch per SQLite

3m 48s

Affronta la sfida di modificare le tabelle in SQLite, che non ha un supporto completo per ALTER TABLE. Impara il flusso di lavoro 'move and copy' utilizzando le operazioni batch di Alembic.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Alembic Database Migrations, episodio 6 di 8. Stai sviluppando un'app in locale, stai testando la tua migration e provi a fare il drop di una singola colonna. Il database lancia un errore e scopri un fatto sorprendente: un semplice comando di drop column fondamentalmente non esiste nel tuo database engine. Questa è la realtà di lavorare con SQLite, ed è esattamente il motivo per cui Alembic offre le Batch Migrations. SQLite ha un'architettura leggera con un supporto molto limitato per alterare le tabelle esistenti. Puoi aggiungere una colonna a una tabella, ma se vuoi fare il drop di una colonna, cambiare il tipo di una colonna o rinominarla, il database engine semplicemente non lo supporta. Molti sviluppatori si imbattono in questo problema quando cercano di eseguire un'operazione standard di drop column nel loro script Alembic. Funziona perfettamente su PostgreSQL, ma su SQLite va in crash. Alembic risolve questa limitazione usando un pattern chiamato workflow move and copy. Dato che il database non può modificare la struttura della tabella in place, Alembic ricostruisce l'intera tabella da zero dietro le quinte. Per usare questa feature, non chiami direttamente i metodi standard. Invece, usi un context manager chiamato batch alter table. Passi il nome della tua tabella a questo context manager, e poi definisci tutte le tue modifiche strutturali all'interno di quel blocco. Quando il blocco finisce l'esecuzione, Alembic prende il controllo e orchestra la sostituzione della tabella. Guardiamo uno scenario specifico. Hai una tabella chiamata user_data, e devi fare il drop di una colonna chiamata bar. All'interno del tuo script, apri il context manager batch alter table per la tabella user_data. All'interno del blocco, gli dai l'istruzione di fare il drop della colonna chiamata bar. Questo è tutto il codice Python che devi scrivere. Nel momento in cui il context manager esce, Alembic genera una sequenza di comandi SQL precisi per eseguire il workflow move and copy. Per prima cosa, Alembic legge la struttura attuale della tua tabella. Genera uno statement create table per una tabella temporanea completamente nuova. Questa nuova tabella ha esattamente lo stesso schema dell'originale, tranne per il fatto che manca la colonna bar. Successivamente, Alembic copia i tuoi dati. Esegue uno statement insert che seleziona tutte le righe esistenti dalla tabella originale e le inserisce nella nuova tabella temporanea. Dato che la colonna bar non esiste più nel nuovo schema, quei dati specifici vengono semplicemente lasciati indietro. Una volta che i dati sono stati copiati in modo sicuro, Alembic fa il drop completo della tabella user_data originale. Infine, rinomina la tabella temporanea di nuovo in user_data. Il database si ritrova esattamente nello stato che volevi, e la tua applicazione non saprà mai che la tabella è stata completamente ricostruita. Ecco il punto chiave. Il context manager batch alter table raggruppa le tue operazioni in batch per le performance. Se devi fare il drop di due colonne, aggiungerne una nuova e cambiare un data type, metti tutte queste istruzioni all'interno dello stesso context block. Alembic compilerà tutte queste modifiche ed eseguirà il workflow move and copy esattamente una volta. Ricostruire una tabella di grandi dimensioni è un'operazione costosa che comporta pesanti letture e scritture su disco, quindi farlo in un singolo passaggio è cruciale. Le operazioni batch trasformano una grave limitazione del database engine in un dettaglio di implementazione completamente invisibile, permettendoti di scrivere script di migration puliti e database-agnostic, mentre Alembic gestisce il lavoro pesante della ricreazione delle tabelle in modo sicuro in background. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
7

Lavorare con i branch

3m 19s

Padroneggia la collaborazione in team gestendo flussi di migrazione ramificati. Scopri come identificare e unire cronologie di revisione divergenti quando più sviluppatori modificano il database.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Alembic Database Migrations, episodio 7 di 8. Un merge di codice incasinato è fastidioso, ma di solito fa solo fallire un test in locale. Due database schema in conflitto, d'altra parte, possono bloccare l'intera deployment pipeline. Fai il merge del tuo codice sul main, ma all'improvviso il tuo tool di database migration si lamenta della presenza di multiple heads. Questa è la realtà quando lavori con i branch in Alembic, e per risolverla devi capire come si biforca la timeline del tuo database. I branch nascono in modo naturale in qualsiasi team. Lo sviluppatore A lavora su una feature e genera un migration script per aggiungere una tabella per il carrello. Questo nuovo script punta allo stato attuale del database, chiamiamolo revision 100, come base. Nel frattempo, lo sviluppatore B lavora su un branch diverso e genera uno script per aggiungere una colonna account. Anche il suo script punta alla revision 100 come base. Entrambi gli sviluppatori testano in locale, tutto funziona bene, e viene fatto il merge di entrambe le pull request nel main repository. Ora nel tuo progetto hai due migration script separati, che dichiarano entrambi di essere il successore immediato della revision 100. La timeline della migration si è divisa in due percorsi paralleli. Se lanci il comando di upgrade del database alla head revision, Alembic si fermerà immediatamente. Lancerà un errore indicando la presenza di multiple heads. Il tool si rifiuta di indovinare quale migration debba essere applicata per prima, perché applicare modifiche al database in un ordine imprevedibile è pericoloso. Per risolvere questo problema, devi riconciliare i flussi divergenti usando il comando merge di Alembic. Ecco il punto chiave. Fare un merge in Alembic non è come fare un merge in Git. Non guarda dentro i file Python cercando di combinare automaticamente le modifiche al tuo schema in un unico file. Invece, il comando merge crea un migration script completamente nuovo e vuoto. Questo nuovo script non contiene operazioni sul database. Non altera tabelle e non aggiunge colonne. Il suo unico scopo è strutturale. All'interno del file Python generato, la variabile down revision viene impostata su una tuple contenente i revision ID di entrambi gli script divergenti, anziché su una singola string. Questa singola azione ricollega i due branch paralleli. Crea una nuova head unificata per la timeline. Quando lanci il comando, in genere gli passi la parola heads, che dice ad Alembic di trovare tutti gli endpoint correnti nella tua migration history e di farne il merge. Puoi anche allegare una message string per documentare la sincronizzazione, un po' come un commit message. Una volta che questo merge script viene generato e committato nel tuo repository, la tua timeline torna a essere lineare. La prossima volta che lanci il comando di upgrade, Alembic eseguirà entrambi i parent script in una sequenza sicura e poi applicherà al database il nuovo merged revision ID. L'integrità strutturale della tua database history dipende da questa sincronizzazione. Un branch di Alembic è semplicemente un fork nella tua migration history, e sistemarlo significa generare uno script dedicato che funge da nodo fisico, ricollegando quei percorsi divergenti in un'unica sequenza chiara. Se trovi utili questi episodi e vuoi supportare lo show, puoi cercare DevStoriesEU su Patreon. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!
8

Power-Up per la produzione

4m 11s

Migliora la tua conoscenza di Alembic con tecniche avanzate. Trattiamo l'invocazione programmatica dei comandi e la condivisione di una connessione con framework applicativi come FastAPI.

Download
Ciao, sono Alex di DEV STORIES DOT EU. Database migrations con Alembic, episodio 8 di 8. Per le moderne applicazioni containerizzate, affidarsi a script manuali da command-line per preparare il database prima dello startup dell'app è un grattacapo operativo. La tua applicazione dovrebbe essere abbastanza intelligente da verificare e aggiornare il proprio schema allo startup. Per farlo in modo sicuro, hai bisogno di Production Power-Ups. La maggior parte dei developer conosce Alembic solo come tool da terminale. Digiti un comando di upgrade e questo modifica il database. Ma la command-line interface è solo un thin wrapper. Sotto c'è la programmatic API di Alembic. Puoi triggerare le migrazioni direttamente dal codice della tua applicazione Python. Questo permette ai moderni framework backend di eseguire automaticamente gli update dello schema durante la loro routine di startup, garantendo che il codice e il database siano sempre perfettamente sincronizzati. Tuttavia, farlo in modo ingenuo introduce un problema sottile. Quando esegui una migrazione a livello programmatico, Alembic usa il suo comportamento di default. Legge il tuo file di configurazione, crea un nuovo database engine, apre una connection, esegue la migrazione e la chiude. Ma la tua applicazione ha appena fatto lo startup. Ha già creato un engine e inizializzato un connection pool. Lasciare che Alembic tiri su una connection completamente separata durante lo startup è inefficiente. Cosa ancora più importante, può essere pericoloso. Se la sequenza di startup della tua applicazione mantiene un lock su una tabella, la connection separata di Alembic rimarrà appesa in attesa di quel lock, causando un deadlock che fa crashare il tuo container. Questo è un problema enorme anche se fai girare test automatizzati contro un database in-memory. In quello scenario, una nuova connection punta a un database completamente vuoto, il che significa che le tue migrazioni non riusciranno ad applicarsi ai dati che stai effettivamente testando. Puoi risolvere questo problema passando la connection attiva al database della tua applicazione direttamente ad Alembic. Questo si fa usando il configuration object di Alembic. Per prima cosa, il codice della tua applicazione istanzia un configuration object, puntandolo al tuo file di inizializzazione principale. Ecco il punto chiave. Il configuration object ha un dictionary degli attributes. Questo fa da ponte per passare oggetti Python live nel migration environment. Acquisisci una connection attiva dal tuo application engine, e la assegni a una key chiamata connection all'interno di quel dictionary degli attributes. Successivamente, chiami la programmatic API di Alembic, in particolare il comando upgrade, passandogli il tuo configuration object modificato e dicendogli di fare l'upgrade alla head revision. Ma Alembic non sa automaticamente cosa fare con quella connection iniettata. Devi modificare il tuo file del migration environment per completare il circuito. Nella sezione del tuo environment file che gestisce le migrazioni online, aggiungi un semplice check prima che avvenga il setup. Istruisci lo script a guardare all'interno dei configuration attributes. Se lì trova un connection object, salta la creazione di un nuovo engine. Invece, configura il migration context per usare la connection che hai fornito. Se non trova una connection negli attributes, fa un fallback sicuro al comportamento normale, creando un nuovo engine dal database URL. Questo fallback garantisce che i tuoi command-line tool continuino a funzionare esattamente come prima quando li fai girare in locale. Progettando il tuo sistema in questo modo, trasformi le migrazioni da un noioso task di deploy esterno a una parte nativa e prevedibile dell'application lifecycle. Quando una nuova istanza fa lo spin up, richiede una connection, gestisce i propri upgrade all'interno di quella session, e passa senza interruzioni a servire il traffico. Questo conclude la nostra serie sulle database migrations. Ti incoraggio vivamente a esplorare la documentazione ufficiale di Alembic e a provare queste programmatic configurations hands-on. Se hai idee per argomenti completamente nuovi che ti piacerebbe ascoltare, visita devstories dot eu e faccelo sapere. Questo è tutto per questo episodio. Grazie per l'ascolto, e continua a sviluppare!