Zurück zum Katalog
Season 38 8 Episoden 33 min 2026

Alembic Database Migrations

v1.18 — 2026 Edition. Meistere Datenbank-Migrationen mit Alembic 1.18 in Python. Lerne, wie man Schema-Änderungen verwaltet, autogenerate nutzt, Constraints handhabt, Offline-Skripte schreibt und Datenbank-Migrationen effektiv zusammen mit SQLAlchemy orchestriert.

Datenbanken Datenbankmigrationen ORM
Alembic Database Migrations
Aktuelle Wiedergabe
Click play to start
0:00
0:00
1
Das Argument für Migrationen
Entdecke, warum manuelles Schema-Management bei zunehmender Skalierung scheitert und wie Alembic Versionskontrolle in deine relationale Datenbank bringt. Wir erkunden das grundlegende mentale Modell von Datenbank-Migrationen und analysieren den Aufbau der Alembic-Umgebung.
4m 04s
2
Anatomie einer Revision
Gehe den Lebenszyklus deiner allerersten Alembic-Migration durch. Wir schlüsseln die upgrade- und downgrade-Funktionen auf und enthüllen, wie die Versionsverfolgung innerhalb der Datenbank tatsächlich funktioniert.
4m 03s
3
Die Magie und Grenzen von autogenerate
Finde heraus, wie Alembic Änderungen automatisch erkennt, indem es deine SQLAlchemy-Modelle mit den Live-Datenbank-Metadaten vergleicht. Erfahre, was es fehlerfrei erfasst und was es übersieht.
3m 55s
4
Die Bedeutung der Benennung von Constraints
Entdecke, warum es ein Rezept für Migrationskatastrophen ist, sich auf von der Datenbank generierte Namen für Constraints zu verlassen. Lerne, wie du eine einheitliche Namenskonvention für dein System konfigurierst.
4m 05s
5
Offline-Migrationen und SQL-Generierung
Erfahre, wie du reine SQL-Skripte für deine Datenbankadministratoren generierst, anstatt Python direkt auf deiner Produktionsdatenbank auszuführen. Wir besprechen den Ablauf der Offline-Ausführung.
4m 37s
6
Batch-Migrationen für SQLite
Meistere die Herausforderung, Tabellen in SQLite zu ändern, da es keine vollständige ALTER TABLE-Unterstützung bietet. Lerne den 'Move and Copy'-Workflow mithilfe der Batch-Operationen von Alembic kennen.
3m 52s
7
Arbeiten mit Branches
Meistere die Teamzusammenarbeit durch den Umgang mit verzweigten Migrationsströmen. Lerne, wie du abweichende Revisionsverläufe identifizierst und zusammenführst, wenn mehrere Entwickler die Datenbank ändern.
3m 57s
8
Produktions-Power-Ups
Erweitere dein Alembic-Wissen mit fortgeschrittenen Techniken. Wir behandeln den programmatischen Befehlsaufruf und das Teilen einer Verbindung mit Anwendungs-Frameworks wie FastAPI.
4m 46s

Episoden

1

Das Argument für Migrationen

4m 04s

Entdecke, warum manuelles Schema-Management bei zunehmender Skalierung scheitert und wie Alembic Versionskontrolle in deine relationale Datenbank bringt. Wir erkunden das grundlegende mentale Modell von Datenbank-Migrationen und analysieren den Aufbau der Alembic-Umgebung.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Alembic Database Migrations, Folge 1 von 8. Du führst einen manuellen Datenbank-Befehl direkt auf deinem Production-Server aus, um eine einzelne Column hinzuzufügen, und plötzlich hängt sich die Anwendung auf. Dein deployter Code hat eine bestimmte Struktur erwartet, die Datenbank hat jetzt eine andere, und es gibt keinen einfachen Undo-Button. Genau deshalb schauen wir uns heute an, warum Migrations so wichtig sind. Wenn ein Softwareprojekt gerade erst startet, ist es einfach, das Datenbank-Schema zu ändern. Du dropst die Tables und erstellst sie neu. Sobald du aber echte User und echte Daten hast, ist das keine Option mehr. Manuelle Änderungen über verschiedene Environments hinweg, wie Development, Staging und Production, führen irgendwann zu einem Mismatch. Dein Application-Code verlässt sich auf einen bestimmten Datenbank-State. Wenn dieser State abweicht, crasht die Anwendung. Alembic löst das, indem es als Version Control für dein Datenbank-Schema fungiert. Genau wie du die History von deinem Source Code trackst, trackt Alembic die History deiner Datenbank-Struktur. Um Alembic zu nutzen, initialisierst du ein Migration-Environment. Das ist eine dedizierte Directory-Struktur, die du zusammen mit deinem Application-Code in dein Source-Control-Repository committest. Sie enthält die Anweisungen und die Konfiguration, die nötig sind, um deine Datenbank im Laufe der Zeit zu modifizieren. Das Environment besteht aus drei Hauptkomponenten. Die erste ist die Root-Konfigurationsdatei namens alembic dot ini. Dieses File liegt im Root-Verzeichnis deines Projekts. Es speichert grundlegende Settings, vor allem die Database Connection URL, die Alembic sagt, wo sich die Target-Datenbank eigentlich befindet. Als Nächstes kommt das versions Directory. Hier werden die Migration-Scripts gespeichert. Jedes Mal, wenn du das Datenbank-Schema ändern musst, wird in diesem Ordner ein neues Python-Script erstellt. Jedes Script definiert zwei Aktionen: eine Upgrade-Funktion, um die Änderung anzuwenden, und eine Downgrade-Funktion, um sie rückgängig zu machen. Wenn du eine Table für User-Profile hinzufügen musst, liegt die genaue Anweisung für diese Änderung in einem Script genau hier. Das letzte Teil ist ein File namens env dot py. Man verwechselt das leicht mit einer allgemeinen Application-Konfigurationsdatei oder einem Ort, um Systemvariablen zu speichern, aber das ist nicht sein Zweck. Hier ist der entscheidende Punkt. Das env dot py File bildet speziell die Brücke zwischen deinen Application-Models und der Alembic Migration-Engine. Es setzt die Database-Engine auf, managt den Connection-Lifecycle und, was am wichtigsten ist, es lädt deine SQLAlchemy-Metadata. Das sagt Alembic genau, wie deine Models im Code aussehen, damit es weiß, welchem Datenbank-Schema es am Ende entsprechen soll. Wann immer du einen Alembic-Command aufrufst, führt Alembic zuerst dieses env dot py Script aus, um den Kontext herzustellen, den es zum Arbeiten braucht. Anstatt dich auf ein fehleranfälliges Gedächtnis für manuelle Datenbank-Commands zu verlassen, hast du einen strukturierten, wiederholbaren Prozess. Der wahre Wert vom Alembic-Environment ist nicht nur, dass es Commands sicher ausführt, sondern dass es eine definitive, versionierte History darüber erstellt, wie sich deine Datenstrukturen vom ersten Tag an entwickelt haben. Wenn dir der Podcast gefällt und du die Show unterstützen möchtest, findest du uns, wenn du auf Patreon nach DevStoriesEU suchst. Ich möchte mir kurz die Zeit nehmen, dir fürs Zuhören zu danken – das hilft uns sehr. Mach's gut!
2

Anatomie einer Revision

4m 03s

Gehe den Lebenszyklus deiner allerersten Alembic-Migration durch. Wir schlüsseln die upgrade- und downgrade-Funktionen auf und enthüllen, wie die Versionsverfolgung innerhalb der Datenbank tatsächlich funktioniert.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Alembic Database Migrations, Folge 2 von 8. Hast du dich jemals gefragt, woher eine Datenbank eigentlich weiß, welche Schema-Version sie gerade ausführt? Sie inspiziert nicht deine Tabellen oder rät basierend darauf, welche Columns existieren. Das Geheimnis liegt in einer einzigen versteckten Tabelle, und zu verstehen, wie diese mit deinem Code zusammenhängt, ist der Kern der Anatomie einer Revision. Um ein Datenbank-Schema zu ändern, erstellst du zunächst ein Migration-Script. Das machst du, indem du den Alembic revision Command zusammen mit einer kurzen beschreibenden Message ausführst, wie zum Beispiel create account table. Alembic generiert eine neue Python-Datei in deinem versions Directory. Der Dateiname beginnt mit einem zufälligen String aus Zeichen, gefolgt von deiner Message. Dieser String ist eine partielle GUID, ein Globally Unique Identifier. Alembic verwendet diese Identifier anstelle von sequenziellen Integers, um Merge Conflicts zu vermeiden, wenn mehrere Entwickler gleichzeitig Migrations in verschiedenen Branches erstellen. Wenn du diese neue Python-Datei öffnest, siehst du oben zwei Variablen: revision und down revision. Die revision Variable enthält die GUID für dieses spezifische Script. Die down revision Variable enthält die GUID des Scripts, das direkt davor kam. Hier ist die entscheidende Erkenntnis: Entwickler denken oft, dass Migrations in der Reihenfolge der Timestamps der Dateierstellung oder nach alphabetischen Dateinamen angewendet werden. Das stimmt nicht. Alembic verlässt sich strikt auf die down revision Chain. Es liest diese Variablen in den Dateien, um eine Linked List deiner Schema-History aufzubauen. Wenn ein Script nicht auf eine gültige vorherige Revision verweist, bricht die Chain. Unterhalb dieser Routing-Variablen findest du zwei leere Funktionen: upgrade und downgrade. Hier schreibst du deine Schema-Änderungen manuell. Für unser Szenario fügen wir eine account Tabelle hinzu. In der upgrade Funktion schreibst du die Logik zum Erstellen der Tabelle und definierst deine Columns, wie einen Integer Primary Key und einen String für den account name. Die downgrade Funktion muss genau das Gegenteil tun. Wenn upgrade die account Tabelle erstellt, muss downgrade sie droppen. Jeder Schritt nach vorn muss einen entsprechenden, zuverlässigen Schritt zurück haben. Sobald dein Script geschrieben ist, wendest du es an, indem du den Alembic upgrade Command ausführst und ihn auf head zeigen lässt, was die aktuellste Revision in deiner Chain bedeutet. Im Hintergrund passiert Folgendes: Alembic verbindet sich mit deiner Datenbank und sucht nach einer Tabelle namens alembic version. Wenn dies deine erste Migration ist, existiert die Tabelle noch nicht, also erstellt Alembic sie. Diese Tabelle hat genau eine Zeile und eine Spalte und speichert die GUID der aktuell angewendeten Revision. Alembic checkt diese Tabelle, sieht, wo die Datenbank gerade steht, und führt die upgrade Funktionen jedes Scripts aus, das nötig ist, um die Target-Revision zu erreichen. Schließlich updatet es die Version-Tabelle mit deiner neuen GUID. Wenn du dein neues account Feature testest und feststellst, dass etwas nicht stimmt, kannst du einen sauberen Rollback machen. Du führst den Alembic downgrade Command aus und übergibst einen relativen Identifier wie minus eins, um eine einzige Revision zurückzugehen. Alembic schaut sich die aktuelle Version in der Datenbank an, findet das entsprechende Script und führt dessen downgrade Funktion aus. Es droppt die account Tabelle und updatet die Version-Tabelle mit der vorherigen GUID. Die wichtigste Erkenntnis ist, dass ein Migration-Script nicht einfach nur eine lose Sammlung von Datenbank-Commands ist. Es ist ein in sich abgeschlossener Node in einer Linked List, der deiner Datenbank einen präzisen Pfad vorgibt, um sich durch die Zeit sowohl vorwärts als auch rückwärts zu bewegen. Danke, dass du ein paar Minuten mit mir verbracht hast. Bis zum nächsten Mal, mach's gut.
3

Die Magie und Grenzen von autogenerate

3m 55s

Finde heraus, wie Alembic Änderungen automatisch erkennt, indem es deine SQLAlchemy-Modelle mit den Live-Datenbank-Metadaten vergleicht. Erfahre, was es fehlerfrei erfasst und was es übersieht.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Alembic Database Migrations, Folge 3 von 8. Sich blind auf ein automatisiertes Tool zu verlassen, um deine Database Migrations zu schreiben, ist der schnellste Weg, versehentlich deine Production Tables zu löschen. Das Problem liegt darin zu verstehen, was das Tool tatsächlich sieht, wenn es deinen Code mit der Realität vergleicht. Und genau das behandeln wir heute in: Die Magie und die Grenzen von Autogenerate. Alembic hat ein Core-Feature namens Autogenerate. Wenn du einen Revision-Command mit dem Autogenerate-Flag ausführst, macht Alembic einen Vergleich. Zuerst verbindet es sich mit deiner Live-Datenbank und inspiziert das aktuelle Schema. Zweitens schaut es sich den Target-State an, der durch die SQLAlchemy-Models in deinem Application-Code definiert ist. Es vergleicht diese beiden States und findet die Unterschiede heraus. Es gibt ein häufiges Missverständnis bei diesem Schritt. Autogenerate wendet diese Änderungen nicht auf magische Weise auf deine Datenbank an. Es schreibt einfach einen Entwurf für ein Python-Script, das die Migration-Operations enthält, von denen es glaubt, dass du sie brauchst, damit die Datenbank zu deinen Models passt. Du musst dieses Candidate-Script überprüfen, bevor du es tatsächlich auf deiner Datenbank ausführst. Wenn es deine Datenbank mit deinen Models vergleicht, erkennt Autogenerate zuverlässig grundlegende strukturelle Änderungen. Wenn du deinem Code eine neue Model-Class hinzufügst, entwirft Alembic eine Instruction, um eine Table zu erstellen. Wenn du eine Model-Class entfernst, entwirft es eine Instruction, um diese Table zu droppen. Es erkennt korrekt, wenn du Columns hinzufügst oder entfernst, wenn du eine Column änderst, um Null-Values zuzulassen, oder wenn du einfache Indexes und Unique Constraints hinzufügst. Bei diesen Standard-Operations spart dir das Feature enorm viel manuelle Tipparbeit. Hier ist der entscheidende Punkt. Autogenerate hat blinde Flecken, weil es keine Gedanken lesen kann. Angenommen, du entscheidest dich, eine bestehende Table in deinen SQLAlchemy-Models umzubenennen. Du updatest den Code und führst den Autogenerate-Command aus, in der Erwartung, dass Alembic einen sicheren Command entwirft, um den Table-Namen zu ändern. Stattdessen schlägt es etwas extrem Gefährliches vor. Es entwirft einen Command, um die alte Table komplett zu droppen, was alle Daten darin zerstört, und entwirft dann einen zweiten Command, um eine brandneue Table mit dem neuen Namen zu erstellen. Alembic macht das, weil es nur sieht, dass der alte Table-Name in der Datenbank existiert, aber in deinen Models fehlt, und ein neuer Table-Name in deinen Models existiert, aber in der Datenbank fehlt. Es hat keine Möglichkeit, die beiden als simplen Rename miteinander zu verknüpfen. Du musst das entworfene Script manuell bearbeiten, um stattdessen eine Rename-Table-Operation zu verwenden. Genau dieselbe Einschränkung gilt für das Umbenennen von Columns. Autogenerate interpretiert eine umbenannte Column als Instruction, die alte zu droppen und eine neue hinzuzufügen. Abgesehen von Renames gibt es Änderungen, die Autogenerate standardmäßig komplett ignoriert. Wenn du den Data Type einer Column änderst, oder wenn du einen Server Default Value modifizierst, wird Alembic diese Unterschiede überspringen. Du kannst das Tool so konfigurieren, dass es Type- und Default-Changes erkennt, aber du musst diese Settings explizit in deiner Environment-Configuration aktivieren. Selbst wenn diese Settings aktiviert sind, wird es niemals Änderungen an Sequence-Objects oder Constraint-Namen erkennen. Der sicherste Weg, mit Autogenerate umzugehen, ist, es als High-Speed-Diktier-Tool zu betrachten, das den Boilerplate für dich übernimmt, und nicht als intelligentes System, das die Absicht hinter deinen Code-Changes versteht. Danke fürs Dabeisein. Hoffe, du hast etwas Neues mitgenommen.
4

Die Bedeutung der Benennung von Constraints

4m 05s

Entdecke, warum es ein Rezept für Migrationskatastrophen ist, sich auf von der Datenbank generierte Namen für Constraints zu verlassen. Lerne, wie du eine einheitliche Namenskonvention für dein System konfigurierst.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Alembic Database Migrations, Folge 4 von 8. Der einfachste Weg, ein Deployment zu ruinieren, ist der Versuch, einen Database Constraint zu droppen, dem du nie einen Namen gegeben hast. Dein Migration Script funktioniert einwandfrei auf deinem lokalen Rechner, aber die Staging-Umgebung stürzt ab und meldet einen fehlenden Foreign Key mit einem unvorhersehbaren Namen wie SYS C 0 0 2 9 3 3 4. Die Ursache ist, dass du dich auf datenbankgenerierte Identifier verlässt. In dieser Folge erfährst du, wie wichtig die Benennung von Constraints ist und wie du das automatisieren kannst. Viele Entwickler gehen davon aus, dass der Object-Relational Mapper das Droppen von Columns und den zugehörigen Regeln problemlos übernimmt. Sie definieren einen Unique Constraint oder einen Foreign Key in ihrem Model, lassen den Namen weg, um Zeit zu sparen, und machen weiter. Wenn du das machst, übernimmt die Database Engine. Systeme wie Postgres oder Oracle weisen automatisch einen beliebigen, systemgenerierten Namen zu, um diese Regel durchzusetzen. Das ist eine tickende Zeitbombe für zukünftige Migrations. Wenn du irgendwann diese Table oder Column ändern oder droppen musst, verwendet Alembic die Drop Constraint Operation. Diese Operation erfordert zwingend den exakten Namen des Target Constraints. Wenn du den Namen von der Datenbank generieren lässt, wird er sich in Development fast sicher von dem in Staging oder Production unterscheiden. Du hardcodest am Ende einen lokalen Constraint-Namen in dein Migration Script, was sofort fehlschlägt, wenn es in einer anderen Umgebung ausgeführt wird, in der dieser zufällige String nicht existiert. Um das zu beheben, muss jeder einzelne Constraint in deiner Datenbank einen expliziten, deterministischen Namen haben. Das manuell für Hunderte von Models zu tun, ist mühsam und wird leicht vergessen. Der bessere Ansatz ist, ein Naming Convention Dictionary in deinem SQLAlchemy MetaData Objekt zu konfigurieren. Dieses Dictionary dient als globales Template für deine Application. Du definierst Regeln für jeden Constraint-Typ. Zum Beispiel kannst du festlegen, dass jeder Index mit dem Prefix i x, gefolgt vom Table-Namen und dem Column-Namen, benannt werden soll. Ähnliche Templates definierst du für Unique Constraints, Check Constraints und Foreign Keys. Danach hängst du dieses konfigurierte MetaData Objekt an deine Declarative Base Class an. Hier kommt der entscheidende Punkt. Sobald dieses Dictionary eingerichtet ist, integriert Alembic deine Naming Conventions automatisch sowohl in sein Autogenerate Feature als auch in seine manuellen Operations. Wenn du einen Command ausführst, um eine neue Migration zu autogenerieren, schaut sich Alembic deine Models an, sieht einen neuen Constraint und checkt das MetaData Dictionary. Es wendet dein Template an, berechnet den expliziten Namen und schreibt genau diesen String in das generierte Python Script. Weil das generierte Script der Datenbank explizit den Command gibt, diesen spezifischen Namen zu verwenden, wird der Constraint in jeder einzelnen Environment identisch sein. Diese Integration erstreckt sich auch auf Alembic Operations, die während des Upgrade-Prozesses selbst ausgeführt werden. Wenn ein Migration Script eine Create Table oder Add Column Operation mit Inline Constraints enthält, denen explizite Namen fehlen, übergibt Alembic diese nicht einfach blind an die Datenbank. Es fängt sie ab, konsultiert das Naming Convention Template und weist den korrekten deterministischen Namen zu, bevor die Database Commands ausgeführt werden. Eine deterministische Naming Convention garantiert, dass eine auf deinem lokalen Rechner erstellte Regel exakt denselben Identifier teilt, wenn sie deine Production Server erreicht, was das Risiko von nicht droppbaren Constraints komplett eliminiert. Danke fürs Zuhören. Macht's gut, Leute.
5

Offline-Migrationen und SQL-Generierung

4m 37s

Erfahre, wie du reine SQL-Skripte für deine Datenbankadministratoren generierst, anstatt Python direkt auf deiner Produktionsdatenbank auszuführen. Wir besprechen den Ablauf der Offline-Ausführung.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Alembic Database Migrations, Folge 5 von 8. In stark regulierten Enterprise-Umgebungen bekommen Entwickler fast nie direkten Zugriff, um Python gegen Production-Datenbanken auszuführen. Wenn der Deployment-Tag ansteht, sperren dich die Datenbank-Administratoren normalerweise aus und verlangen stattdessen ein rohes, überprüfbares Script. Offline Migrations und SQL Generation schließen diese Lücke zwischen deiner Python Codebase und ihrem strengen Deployment-Prozess. Normalerweise verbindet sich Alembic mit einer Live-Datenbank und führt Schema-Änderungen direkt über diese Connection aus. Aber wenn du dem DBA-Team eine Plain-Text-Datei übergeben musst, nutzt du den Offline-Modus. Indem du das dash dash sql Flag an deine upgrade oder downgrade Commands im Terminal anhängst, ändert Alembic sein Ausführungsverhalten komplett. Anstatt die Statements gegen eine Database Engine auszuführen, rendert es sie als durchgehenden String aus Standard-SQL und gibt sie direkt auf dem Standard Output aus. Du kannst diesen Terminal Output ganz einfach in eine Textdatei umleiten. Dieses duale Verhalten ist keine Magie, es ist explizit in deiner Project Environment Datei definiert, die typischerweise env dot py heißt. Wenn du in diese Datei schaust, findest du zwei verschiedene Routing-Funktionen. Die erste ist run migrations online. Diese Funktion erstellt eine Live Database Engine, bindet eine aktive Connection an den Alembic Context und führt deine Migration Scripts Schritt für Schritt aus. Die zweite Funktion ist run migrations offline, und hier passiert die Übersetzung. Wenn du das sql Flag übergibst, erkennt Alembic das Flag und triggert stattdessen diese Offline-Funktion. Sie konfiguriert den Context nur mit einer Database URL. Es wird keine Network Connection aufgebaut und keine Engine instanziiert. Dann nimmt sie deine Python Migration Structures und generiert die exakten CREATE, ALTER oder DROP Statements, packt sie in Standard BEGIN und COMMIT Transaction Blocks und formatiert alles für deinen spezifischen Database Dialect. Hier ist die wichtigste Erkenntnis. Weil der Offline-Modus sich nie wirklich mit der Datenbank verbindet, können sich deine Migration Scripts nicht auf den aktiven Database State verlassen. Du kannst innerhalb einer Offline Migration kein SELECT Statement ausführen, um zu prüfen, ob eine Row existiert, und du kannst den aktuellen State einer Table nicht inspizieren, bevor du eine Änderung machst. Wenn dein Python-Code erwartet, dass ein Database Cursor Daten zurückgibt, um zu entscheiden, welche Schema-Änderung gemacht werden soll, wird die Offline Generation fehlschlagen. Das Script muss rein deklarativ sein. Es sagt Alembic einfach nur, welche Structures generiert werden sollen. Stell dir einen Entwickler vor, der einen lokalen Feature Branch abschließt. Er hat die Migrations lokal im Online-Modus ausgeführt, um zu verifizieren, dass alles gegen seine Testing Database funktioniert. Für das Production Release führt er den upgrade Command mit einer spezifischen Start Revision und End Revision aus, fügt das sql Flag hinzu und leitet den Output in eine Textdatei um. Das Ergebnis ist ein sauberes, sequenzielles SQL Script. Der Entwickler übergibt diese Datei an das DBA-Team. Die DBAs können sie lesen, gegen ihre strengen Security Policies verifizieren und sie während des Maintenance Windows mit Standard Database Administration Tools anwenden. Du hast auch die Kontrolle darüber, wie dieser Offline Output generiert wird. Innerhalb der run migrations offline Funktion akzeptiert der context configure Call Parameter, die das gerenderte SQL anpassen. Ein häufiges Requirement ist das Konvertieren von Variablen in Literal Values. Indem du literal binds in der Configuration aktivierst, stellst du sicher, dass alle Daten, die während der Migration eingefügt werden, die tatsächlichen Values direkt im SQL String enthalten, anstatt generische Parameter Markers auszugeben. Das stellt sicher, dass der Output ein komplett eigenständiges Script ist, das bereit zur Execution ist. Der wahre Wert der Offline Generation ist Predictability; sie verwandelt dynamische Python State Changes in statisches, auditierbares SQL, das jede Deployment Pipeline oder jedes Security Team verifizieren kann, bevor auch nur eine einzige Table modifiziert wird. Das war's für diese Folge. Danke fürs Zuhören und keep building!
6

Batch-Migrationen für SQLite

3m 52s

Meistere die Herausforderung, Tabellen in SQLite zu ändern, da es keine vollständige ALTER TABLE-Unterstützung bietet. Lerne den 'Move and Copy'-Workflow mithilfe der Batch-Operationen von Alembic kennen.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Alembic Database Migrations, Folge 6 von 8. Du baust eine lokale App, testest deine Migration und versuchst, eine einzelne Column zu droppen. Die Datenbank wirft einen Error, und du entdeckst eine überraschende Tatsache: Ein einfacher Drop Column Command existiert in deiner Database Engine grundsätzlich nicht. Das ist die Realität bei der Arbeit mit SQLite, und genau deshalb bietet Alembic Batch Migrations an. SQLite hat eine schlanke Architektur mit sehr eingeschränktem Support für das Ändern bestehender Tabellen. Du kannst zwar eine Column zu einer Tabelle hinzufügen, aber wenn du eine Column droppen, den Column Type ändern oder eine Column umbenennen willst, wird das von der Database Engine einfach nicht supportet. Viele Developer stoßen darauf, wenn sie versuchen, eine Standard Drop Column Operation in ihrem Alembic Script auszuführen. Auf PostgreSQL funktioniert das einwandfrei, aber auf SQLite crasht es. Alembic löst diese Einschränkung mit einem Pattern namens Move and Copy Workflow. Da die Datenbank die Tabellenstruktur nicht in place ändern kann, baut Alembic die komplette Tabelle im Hintergrund von Grund auf neu. Um dieses Feature zu nutzen, rufst du die Standard Operation Methods nicht direkt auf. Stattdessen nutzt du einen Context Manager namens batch alter table. Du übergibst diesem Context Manager den Namen deiner Tabelle und definierst dann all deine strukturellen Änderungen innerhalb dieses Blocks. Wenn der Block fertig ausgeführt ist, übernimmt Alembic und orchestriert das Table Replacement. Schauen wir uns ein konkretes Szenario an. Du hast eine Tabelle namens user_data und musst eine Column namens bar droppen. In deinem Script öffnest du den batch alter table Context Manager für die user_data Tabelle. Innerhalb des Blocks weist du ihn an, die Column namens bar zu droppen. Das ist der gesamte Python-Code, den du schreibst. In dem Moment, in dem der Context Manager beendet wird, generiert Alembic eine Sequenz von präzisen SQL Commands, um den Move and Copy Workflow auszuführen. Zuerst liest Alembic die aktuelle Struktur deiner Tabelle. Es generiert ein create table Statement für eine brandneue, temporäre Tabelle. Diese neue Tabelle hat exakt dasselbe Schema wie das Original, nur dass die bar Column fehlt. Als Nächstes kopiert Alembic deine Daten. Es führt ein insert Statement aus, das alle existierenden Rows aus der Originaltabelle selectet und in die neue temporäre Tabelle pusht. Weil die bar Column im neuen Schema nicht mehr existiert, werden diese spezifischen Daten einfach weggelassen. Sobald die Daten sicher rüberkopiert wurden, droppt Alembic die originale user_data Tabelle komplett. Zum Schluss benennt es die temporäre Tabelle wieder in user_data um. Die Datenbank landet exakt in dem State, den du wolltest, und deine Application merkt nie, dass die Tabelle komplett neu gebaut wurde. Hier ist der entscheidende Punkt. Der batch alter table Context Manager batcht deine Operations für eine bessere Performance zusammen. Wenn du zwei Columns droppen, eine neue hinzufügen und einen Data Type ändern musst, packst du all diese Instructions in denselben Context Block. Alembic kompiliert all diese Changes und führt den Move and Copy Workflow exakt einmal aus. Das Rebuilden einer großen Tabelle ist eine teure Operation, die heavy Disk Reads und Writes erfordert, deshalb ist es entscheidend, das in einem einzigen Durchlauf zu machen. Batch Operations machen aus einer gravierenden Engine-Einschränkung ein völlig unsichtbares Implementation Detail. So kannst du cleane, database-agnostic Migration Scripts schreiben, während Alembic das Heavy Lifting der Table Recreation sicher im Hintergrund übernimmt. Das war's für diese Folge. Danke fürs Zuhören und keep building!
7

Arbeiten mit Branches

3m 57s

Meistere die Teamzusammenarbeit durch den Umgang mit verzweigten Migrationsströmen. Lerne, wie du abweichende Revisionsverläufe identifizierst und zusammenführst, wenn mehrere Entwickler die Datenbank ändern.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Alembic-Datenbankmigrationen, Folge 7 von 8. Ein unsauberer Code-Merge ist ärgerlich, aber meistens schlägt dann einfach nur ein lokaler Test fehl. Zwei in Konflikt stehende Datenbankschemata hingegen können deine gesamte Deployment-Pipeline zum Stillstand bringen. Du mergst deinen Code nach main, aber plötzlich beschwert sich dein Datenbank-Migration-Tool über mehrere Heads. Das ist die Realität beim Arbeiten mit Branches in Alembic, und die Lösung erfordert ein Verständnis dafür, wie sich deine Datenbank-Timeline verzweigt. Branches entstehen in jeder Teamumgebung ganz natürlich. Entwickler A arbeitet an einem Feature und generiert ein Migration-Script zum Hinzufügen einer Shopping-Cart-Tabelle. Dieses neue Script verwendet den aktuellen Datenbankstatus, nennen wir ihn Revision 100, als Basis. Gleichzeitig arbeitet Entwickler B in einem anderen Branch und generiert ein Script zum Hinzufügen einer Account-Spalte. Auch sein Script verwendet Revision 100 als Basis. Beide Entwickler testen lokal, alles funktioniert einwandfrei, und beide Pull Requests werden in das Main-Repository gemergt. Du hast jetzt zwei separate Migration-Scripts in deinem Projekt, die beide behaupten, der direkte Nachfolger von Revision 100 zu sein. Die Migration-Timeline hat sich in zwei parallele Pfade aufgespalten. Wenn du den Command ausführst, um die Datenbank auf die Head-Revision upzugraden, stoppt Alembic sofort. Es wirft einen Error, der besagt, dass es mehrere Heads gibt. Das Tool weigert sich zu raten, welche Migration zuerst angewendet werden soll, denn Datenbankänderungen in einer unvorhersehbaren Reihenfolge anzuwenden, ist gefährlich. Um das zu lösen, musst du die abweichenden Stränge mit dem Alembic Merge-Command zusammenführen. Hier ist die entscheidende Erkenntnis. Ein Merge in Alembic ist nicht wie ein Git-Merge. Es schaut nicht in die Python-Files und versucht, deine Schema-Änderungen automatisch in einem einzigen File zu kombinieren. Stattdessen erstellt der Merge-Command ein komplett neues, leeres Migration-Script. Dieses neue Script enthält keine Datenbankoperationen. Es ändert keine Tabellen und fügt keine Spalten hinzu. Sein einziger Zweck ist struktureller Natur. In dem generierten Python-File wird die down revision Variable auf ein Tuple mit den Revision-IDs beider abweichender Scripts gesetzt, anstatt auf einen einzelnen String. Diese einzige Aktion bindet die beiden parallelen Branches wieder zusammen. Es erstellt einen neuen, vereinten Head für die Timeline. Wenn du den Command ausführst, übergibst du normalerweise das Wort heads, was Alembic anweist, alle aktuellen Endpoints in deiner Migration-History zu finden und zu mergen. Du kannst auch einen Message-String anhängen, um die Synchronisation zu dokumentieren, ganz ähnlich wie bei einer Commit-Message. Sobald dieses Merge-Script generiert und in dein Repository gecommittet wurde, ist deine Timeline wieder linear. Wenn du das nächste Mal den Upgrade-Command ausführst, führt Alembic beide Parent-Scripts in einer sicheren Reihenfolge aus und stempelt die Datenbank dann mit der neuen, gemergten Revision-ID. Die strukturelle Integrität deiner Datenbank-History hängt von dieser Synchronisation ab. Ein Alembic-Branch ist einfach nur ein Fork in deiner Migration-History, und ihn zu fixen bedeutet, ein dediziertes Script zu generieren, das wie ein physischer Knoten wirkt und diese abweichenden Pfade wieder zu einer klaren Sequenz zusammenbindet. Wenn du diese Episoden hilfreich findest und die Show unterstützen möchtest, kannst du auf Patreon nach DevStoriesEU suchen. Das war es für diese Folge. Danke fürs Zuhören und keep building!
8

Produktions-Power-Ups

4m 46s

Erweitere dein Alembic-Wissen mit fortgeschrittenen Techniken. Wir behandeln den programmatischen Befehlsaufruf und das Teilen einer Verbindung mit Anwendungs-Frameworks wie FastAPI.

Herunterladen
Hallo, hier ist Alex von DEV STORIES DOT EU. Alembic Database Migrations, Folge 8 von 8. Für moderne, containerisierte Apps ist es ein operativer Albtraum, sich auf manuelle Command-Line Scripts zu verlassen, um deine Datenbank vor dem App-Start vorzubereiten. Deine App sollte smart genug sein, ihr eigenes Schema beim Startup selbst zu überprüfen und zu updaten. Um das sicher zu tun, brauchst du Production Power-Ups. Die meisten Developer kennen Alembic nur als Terminal-Tool. Du tippst einen Upgrade Command ein, und es modifiziert die Datenbank. Aber das Command-Line Interface ist nur ein dünner Wrapper. Darunter verbirgt sich die programmatische API von Alembic. Du kannst Migrations direkt aus deinem Python App-Code triggern. Das erlaubt es modernen Backend Frameworks, Schema-Updates automatisch während ihrer Startup-Routine auszuführen. So wird sichergestellt, dass der Code und die Datenbank immer perfekt synchronisiert sind. Wenn du das allerdings naiv angehst, entsteht ein subtiles Problem. Wenn du eine Migration programmatisch ausführst, fällt Alembic auf sein Standardverhalten zurück. Es liest dein Configuration File, erstellt eine brandneue Database Engine, öffnet eine Connection, führt die Migration aus und schließt sie wieder. Aber deine App ist ja gerade erst gestartet. Sie hat bereits eine Engine erstellt und einen Connection Pool initialisiert. Alembic beim Startup eine komplett separate Connection aufbauen zu lassen, ist ineffizient. Noch wichtiger ist: Es kann gefährlich sein. Wenn deine Startup-Sequenz einen Lock auf einer Tabelle hält, bleibt die separate Connection von Alembic hängen, weil sie auf diesen Lock wartet. Das verursacht einen Deadlock, der deinen Container crashen lässt. Das ist auch ein riesiges Problem, wenn du automatisierte Tests gegen eine In-Memory-Datenbank laufen lässt. In diesem Szenario zeigt eine brandneue Connection auf eine komplett leere Datenbank. Das bedeutet, dass deine Migrations nicht auf die Daten angewendet werden, die du eigentlich testest. Du löst das, indem du die aktive Database Connection deiner App direkt an Alembic übergibst. Das machst du über das Alembic Configuration Object. Zuerst instanziiert dein App-Code ein Configuration Object und lässt es auf dein Main Initialization File zeigen. Hier ist der entscheidende Punkt: Das Configuration Object hat ein Attributes Dictionary. Das fungiert als Brücke, um Live-Python-Objekte in das Migration Environment zu übergeben. Du holst dir eine aktive Connection von deiner App-Engine und weist sie einem Key namens connection innerhalb dieses Attributes Dictionary zu. Als Nächstes rufst du die programmatische API von Alembic auf, genauer gesagt den Upgrade Command. Du übergibst dein modifiziertes Configuration Object und sagst ihm, dass es auf die Head Revision upgraden soll. Aber Alembic weiß nicht automatisch, was es mit dieser injected Connection machen soll. Du musst dein Migration Environment File anpassen, um den Kreis zu schließen. In der Section deines Environment Files, die Online Migrations verarbeitet, fügst du einen simplen Check hinzu, bevor das Setup passiert. Du sagst dem Script, dass es in die Configuration Attributes schauen soll. Wenn es dort ein Connection Object findet, überspringt es das Erstellen einer neuen Engine. Stattdessen konfiguriert es den Migration Context so, dass er die Connection nutzt, die du bereitgestellt hast. Wenn es keine Connection in den Attributes findet, macht es einen sicheren Fallback auf das normale Verhalten und erstellt eine neue Engine aus der Database URL. Dieser Fallback stellt sicher, dass deine Command-Line Tools weiterhin genau wie vorher funktionieren, wenn du sie lokal ausführst. Indem du dein System so designst, verwandelst du Migrations von einer externen Deployment-Aufgabe in einen nativen, berechenbaren Teil des Application Lifecycles. Wenn eine neue Instance hochfährt, fordert sie eine Connection an, handelt ihre eigenen Upgrades innerhalb dieser Session ab und geht nahtlos dazu über, Traffic zu bedienen. Das schließt unsere Serie über Database Migrations ab. Ich empfehle dir sehr, die offizielle Alembic Documentation zu erkunden und diese programmatischen Configurations einfach mal hands-on auszuprobieren. Wenn du Ideen für komplett neue Themen hast, die du gerne hören würdest, besuche devstories dot eu und lass es uns wissen. Das war es für diese Folge. Danke fürs Zuhören und keep building!