Wróć do katalogu
Season 38 8 Odcinki 32 min 2026

Alembic Database Migrations

v1.18 — Edycja 2026. Opanuj migracje baz danych za pomocą Alembic 1.18 w Pythonie. Dowiedz się, jak zarządzać zmianami schematu, używać autogenerate, obsługiwać ograniczenia, pisać skrypty offline i skutecznie orkiestrować migracje baz danych we współpracy z SQLAlchemy.

Bazy danych Migracje baz danych ORM
Alembic Database Migrations
Teraz odtwarzane
Click play to start
0:00
0:00
1
Argumenty za migracjami
Odkryj, dlaczego ręczne zarządzanie schematem zawodzi na dużą skalę i jak Alembic wprowadza kontrolę wersji do twojej relacyjnej bazy danych. Badamy podstawowy model mentalny migracji baz danych i rozkładamy na czynniki pierwsze anatomię środowiska Alembic.
3m 47s
2
Anatomia rewizji
Prześledź cykl życia swojej pierwszej migracji w Alembic. Analizujemy funkcje upgrade i downgrade oraz ujawniamy, jak naprawdę działa śledzenie wersji wewnątrz bazy danych.
3m 57s
3
Magia i ograniczenia autogenerate
Odkryj, jak Alembic automatycznie wykrywa zmiany, porównując twoje modele SQLAlchemy z metadanymi działającej bazy danych. Dowiedz się, co wyłapuje bezbłędnie, a co pomija.
3m 37s
4
Znaczenie nazewnictwa ograniczeń
Odkryj, dlaczego poleganie na nazwach ograniczeń generowanych przez bazę danych to gotowy przepis na katastrofę podczas migracji. Dowiedz się, jak skonfigurować ujednoliconą konwencję nazewnictwa dla swojego systemu.
4m 16s
5
Migracje offline i generowanie SQL
Sprawdź, jak generować czyste skrypty SQL dla administratorów baz danych, zamiast uruchamiać Pythona bezpośrednio na bazie produkcyjnej. Omawiamy przepływ wykonywania w trybie offline.
4m 35s
6
Migracje batchowe dla SQLite
Zmierz się z wyzwaniem modyfikowania tabel w SQLite, któremu brakuje pełnego wsparcia dla ALTER TABLE. Poznaj przepływ pracy move and copy, wykorzystując operacje batchowe w Alembic.
3m 33s
7
Praca z gałęziami
Opanuj współpracę w zespole poprzez obsługę rozgałęzionych strumieni migracji. Dowiedz się, jak identyfikować i scalać rozbieżne historie rewizji, gdy wielu programistów modyfikuje bazę danych.
4m 01s
8
Produkcyjne usprawnienia
Poszerz swoją wiedzę o Alembic dzięki zaawansowanym technikom. Omawiamy programistyczne wywoływanie poleceń i współdzielenie połączenia z frameworkami aplikacyjnymi, takimi jak FastAPI.
4m 15s

Odcinki

1

Argumenty za migracjami

3m 47s

Odkryj, dlaczego ręczne zarządzanie schematem zawodzi na dużą skalę i jak Alembic wprowadza kontrolę wersji do twojej relacyjnej bazy danych. Badamy podstawowy model mentalny migracji baz danych i rozkładamy na czynniki pierwsze anatomię środowiska Alembic.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Migracje bazy danych w Alembic, odcinek 1 z 8. Uruchamiasz ręcznie polecenie na bazie danych bezpośrednio na serwerze produkcyjnym, żeby dodać jedną kolumnę, i nagle aplikacja się zawiesza. Twój kod po deployu oczekiwał jednej struktury, baza danych ma teraz inną, i nie ma prostego przycisku cofania. Właśnie dlatego przyjrzymy się dzisiaj tematowi migracji. Na samym początku projektu zmiana schematu bazy danych jest prosta. Dropujesz tabele i tworzysz je od nowa. Ale kiedy masz już prawdziwych użytkowników i prawdziwe dane, to przestaje wchodzić w grę. Wprowadzanie ręcznych zmian na różnych środowiskach, takich jak development, staging i produkcja, w końcu prowadzi do rozjazdów. Kod twojej aplikacji polega na konkretnym stanie bazy danych. Kiedy ten stan się rozjeżdża, aplikacja przestaje działać. Alembic rozwiązuje ten problem, działając jako system kontroli wersji dla schematu twojej bazy danych. Tak samo jak śledzisz historię swojego kodu źródłowego, Alembic śledzi historię struktury twojej bazy danych. Żeby zacząć korzystać z Alembic, inicjalizujesz środowisko migracji. To dedykowana struktura katalogów, którą komitujesz do repozytorium razem z kodem swojej aplikacji. Zawiera ona instrukcje i konfigurację potrzebną do modyfikowania bazy danych w miarę upływu czasu. To środowisko składa się z trzech głównych elementów. Pierwszy to główny plik konfiguracyjny o nazwie alembic dot ini. Ten plik znajduje się w głównym katalogu twojego projektu. Przechowuje podstawowe ustawienia, przede wszystkim URL połączenia z bazą danych, który mówi Alembicowi, gdzie faktycznie znajduje się docelowa baza. Kolejny element to katalog versions. To tutaj przechowywane są skrypty migracji. Za każdym razem, gdy musisz zmienić schemat bazy danych, w tym folderze tworzony jest nowy skrypt w Pythonie. Każdy skrypt definiuje dwie akcje: funkcję upgrade, która aplikuje zmianę, i funkcję downgrade, która ją cofa. Jeśli musisz dodać tabelę dla profili użytkowników, dokładna instrukcja tej zmiany znajduje się w skrypcie właśnie tutaj. Ostatnim elementem jest plik o nazwie env dot py. Łatwo pomylić go z ogólnym plikiem konfiguracyjnym aplikacji albo miejscem do przechowywania zmiennych systemowych, ale to nie jest jego cel. Oto kluczowa sprawa. Plik env dot py stanowi pomost między modelami twojej aplikacji a silnikiem migracji Alembic. Konfiguruje silnik bazy danych, zarządza cyklem życia połączenia i, co najważniejsze, ładuje twoje metadane SQLAlchemy. To mówi Alembicowi dokładnie, jak twoje modele wyglądają w kodzie, dzięki czemu wie, do jakiego schematu bazy danych ma ostatecznie dążyć. Za każdym razem, gdy odpalasz polecenie Alembic, najpierw uruchamia on ten skrypt env dot py, żeby zbudować kontekst potrzebny do działania. Zamiast polegać na zawodnej pamięci do ręcznych poleceń bazodanowych, masz ustrukturyzowany, powtarzalny proces. Prawdziwą wartością środowiska Alembic nie jest tylko to, że bezpiecznie uruchamia polecenia, ale to, że tworzy ostateczną, wersjonowaną historię tego, jak dokładnie ewoluowały twoje struktury danych od pierwszego dnia. Jeśli podoba ci się ten podcast i chcesz wesprzeć nasz program, znajdziesz nas, wpisując DevStoriesEU na Patreon. Chciałbym poświęcić chwilę, żeby podziękować ci za to, że nas słuchasz — to bardzo nam pomaga. Miłego dnia!
2

Anatomia rewizji

3m 57s

Prześledź cykl życia swojej pierwszej migracji w Alembic. Analizujemy funkcje upgrade i downgrade oraz ujawniamy, jak naprawdę działa śledzenie wersji wewnątrz bazy danych.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Migracje baz danych w Alembic, odcinek 2 z 8. Czy zastanawiałeś się kiedyś, skąd baza danych tak naprawdę wie, na jakiej wersji schematu aktualnie działa? Nie analizuje twoich tabel ani nie zgaduje na podstawie tego, jakie kolumny istnieją. Sekret tkwi w jednej, ukrytej tabeli, a zrozumienie, jak łączy się ona z twoim kodem, to sedno Anatomii Rewizji. Aby zmienić schemat bazy danych, najpierw tworzysz skrypt migracji. Robisz to, odpalając polecenie Alembic revision z krótkim opisem, na przykład create account table. Alembic generuje nowy plik Pythona w twoim katalogu versions. Nazwa pliku zaczyna się od losowego stringa, po którym następuje twój opis. Ten string to częściowy GUID, czyli globalnie unikalny identyfikator. Alembic używa tych identyfikatorów zamiast kolejnych integerów, aby uniknąć merge konfliktów, gdy wielu developerów tworzy migracje na różnych branchach w tym samym czasie. Jeśli otworzysz ten nowy plik Pythona, na samej górze zobaczysz dwie zmienne: revision i down revision. Zmienna revision przechowuje GUID dla tego konkretnego skryptu. Zmienna down revision przechowuje GUID skryptu, który był tuż przed nim. I tu jest kluczowa sprawa. Developerzy często myślą, że migracje są wykonywane w kolejności na podstawie timestampów utworzenia pliku albo alfabetycznych nazw plików. Wcale tak nie jest. Alembic ściśle polega na chainie down revision. Odczytuje te zmienne wewnątrz plików, aby zbudować linked listę historii twojego schematu. Jeśli skrypt nie wskazuje na prawidłową poprzednią rewizję, chain się urywa. Poniżej tych zmiennych routingu znajdziesz dwie puste funkcje: upgrade i downgrade. To tutaj ręcznie piszesz zmiany w schemacie. W naszym scenariuszu dodajemy tabelę account. W funkcji upgrade piszesz logikę tworzenia tabeli, definiując kolumny, takie jak primary key typu integer i string dla nazwy konta. Funkcja downgrade musi robić dokładnie odwrotność. Jeśli upgrade tworzy tabelę account, downgrade musi ją zdropować. Każdy krok w przód musi mieć odpowiadający mu, niezawodny krok w tył. Kiedy twój skrypt jest gotowy, aplikujesz go, odpalając polecenie Alembic upgrade, wskazując na head, co oznacza najnowszą rewizję w twoim chainie. Oto co dzieje się pod spodem. Alembic łączy się z twoją bazą danych i szuka tabeli o nazwie alembic version. Jeśli to twoja pierwsza migracja, ta tabela jeszcze nie istnieje, więc Alembic ją tworzy. Ta tabela ma dokładnie jeden wiersz i jedną kolumnę, w której przechowuje GUID aktualnie zaaplikowanej rewizji. Alembic sprawdza tę tabelę, patrzy, w jakim miejscu aktualnie znajduje się baza danych, i odpala funkcje upgrade każdego skryptu potrzebnego do osiągnięcia docelowej rewizji. Na koniec aktualizuje tabelę wersji twoim nowym GUID. Jeśli przetestujesz swój nowy feature konta i zorientujesz się, że coś jest nie tak, możesz czysto zrobić rollback. Odpalasz polecenie Alembic downgrade, przekazując relatywny identyfikator, taki jak minus jeden, aby cofnąć się o jedną rewizję. Alembic sprawdza obecną wersję w bazie danych, znajduje odpowiedni skrypt i odpala jego funkcję downgrade. Dropuje tabelę account i aktualizuje tabelę wersji poprzednim GUID. Najważniejszy wniosek jest taki, że skrypt migracji to nie tylko luźny zbiór komend bazodanowych. To samodzielny node w linked liście, który daje twojej bazie danych precyzyjną ścieżkę do poruszania się w czasie, zarówno w przód, jak i w tył. Dzięki za spędzenie ze mną tych kilku minut. Do usłyszenia następnym razem, trzymaj się.
3

Magia i ograniczenia autogenerate

3m 37s

Odkryj, jak Alembic automatycznie wykrywa zmiany, porównując twoje modele SQLAlchemy z metadanymi działającej bazy danych. Dowiedz się, co wyłapuje bezbłędnie, a co pomija.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Alembic Database Migrations, odcinek 3 z 8. Ślepe ufanie zautomatyzowanemu narzędziu do pisania migracji bazy danych to prosta droga do przypadkowego usunięcia tabel na produkcji. Problem polega na zrozumieniu, co to narzędzie tak naprawdę widzi, gdy porównuje twój kod z rzeczywistością, i właśnie to omówimy dzisiaj w temacie: Magia i ograniczenia funkcji autogenerate. Alembic ma kluczową funkcję o nazwie autogenerate. Kiedy odpalasz komendę revision z flagą autogenerate, Alembic przeprowadza porównanie. Najpierw łączy się z twoją działającą bazą danych i analizuje obecny schemat. Po drugie, patrzy na stan docelowy zdefiniowany przez modele SQLAlchemy w kodzie twojej aplikacji. Porównuje te dwa stany i wyłapuje różnice. Z tym krokiem wiąże się pewne powszechne nieporozumienie. Autogenerate nie wprowadza magicznie tych zmian do twojej bazy danych. Po prostu pisze draft skryptu w Pythonie, zawierający operacje migracji, które według niego są potrzebne, żeby baza danych pasowała do twoich modeli. Musisz przejrzeć ten wygenerowany skrypt, zanim faktycznie wykonasz go na swojej bazie danych. Kiedy porównuje twoją bazę danych z modelami, autogenerate niezawodnie wykrywa podstawowe zmiany strukturalne. Jeśli dodasz nową klasę modelu do kodu, Alembic przygotuje draft instrukcji create table. Jeśli usuniesz klasę modelu, przygotuje draft instrukcji drop table. Poprawnie wyłapuje, kiedy dodajesz lub usuwasz kolumny, kiedy zmieniasz kolumnę, żeby pozwalała na wartości null, albo kiedy dodajesz podstawowe indeksy i unique constraints. Przy tych standardowych operacjach, ta funkcja oszczędza ci mnóstwo ręcznego pisania. A teraz najważniejsza część. Autogenerate ma swoje martwe punkty, bo nie potrafi czytać w twoich myślach. Załóżmy, że decydujesz się na zmianę nazwy istniejącej tabeli w twoich modelach SQLAlchemy. Aktualizujesz kod i odpalasz komendę autogenerate, oczekując, że Alembic przygotuje draft bezpiecznej komendy do zmiany nazwy tabeli. Zamiast tego proponuje coś bardzo niebezpiecznego. Tworzy draft komendy, żeby całkowicie usunąć starą tabelę, niszcząc wszystkie dane w środku, a następnie tworzy draft drugiej komendy, żeby utworzyć zupełnie nową tabelę z nową nazwą. Alembic robi to, ponieważ widzi tylko, że stara nazwa tabeli istnieje w bazie danych, ale brakuje jej w twoich modelach, a nowa nazwa tabeli istnieje w modelach, ale brakuje jej w bazie danych. Nie ma sposobu, żeby połączyć te dwa fakty w prostą operację rename. Musisz ręcznie zedytować wygenerowany draft skryptu, żeby zamiast tego użyć operacji rename table. Dokładnie to samo ograniczenie dotyczy zmiany nazw kolumn. Autogenerate zinterpretuje zmienioną nazwę kolumny jako instrukcję, żeby usunąć starą i dodać nową. Poza operacjami rename, są też zmiany, które autogenerate domyślnie całkowicie zignoruje. Jeśli zmienisz typ danych kolumny, albo zmodyfikujesz wartość server default, Alembic pominie te różnice. Możesz skonfigurować to narzędzie tak, żeby wykrywało zmiany typów i wartości defaultowych, ale musisz jawnie włączyć te ustawienia w konfiguracji swojego środowiska. Nawet z włączonymi tymi ustawieniami, nigdy nie wykryje zmian w obiektach sequence ani w nazwach constraintów. Najbezpieczniej jest traktować autogenerate jako szybkie narzędzie do dyktowania, które ogarnia za ciebie boilerplate, a nie jak inteligentny system, który rozumie intencje stojące za zmianami w twoim kodzie. Dzięki za wspólnie spędzony czas. Mam nadzieję, że dowiedziałeś się czegoś nowego.
4

Znaczenie nazewnictwa ograniczeń

4m 16s

Odkryj, dlaczego poleganie na nazwach ograniczeń generowanych przez bazę danych to gotowy przepis na katastrofę podczas migracji. Dowiedz się, jak skonfigurować ujednoliconą konwencję nazewnictwa dla swojego systemu.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Alembic Database Migrations, odcinek 4 z 8. Najprostszym sposobem na zepsucie deployu jest próba usunięcia constrainta w bazie danych, któremu tak naprawdę nigdy nie nadałeś nazwy. Twój skrypt migracji działa idealnie na lokalnej maszynie, ale staging właśnie się wywalił, rzucając błędem o brakującym foreign keyu o jakiejś nieprzewidywalnej nazwie, na przykład SYS C 0 0 2 9 3 3 4. Winne jest poleganie na identyfikatorach generowanych przez bazę. W tym odcinku omówimy, dlaczego nazywanie constraintów jest tak ważne i jak to zautomatyzować. Wielu developerów zakłada, że ORM bezproblemowo ogarnia dropowanie kolumn i powiązanych z nimi reguł. Definiują unique constraint albo foreign key w swoim modelu, pomijają nazwę, żeby zaoszczędzić czas, i idą dalej. Kiedy tak robisz, silnik bazy danych przejmuje kontrolę. Systemy takie jak Postgres czy Oracle automatycznie przypiszą przypadkową, wygenerowaną przez system nazwę, żeby wymusić tę regułę. To tworzy tykającą bombę zegarową dla przyszłych migracji. Kiedy w końcu będziesz musiał zrobić alter lub drop tej tabeli albo kolumny, Alembic użyje operacji drop constraint. Ta operacja bezwzględnie wymaga dokładnej nazwy docelowego constrainta. Jeśli pozwolisz bazie danych wygenerować nazwę, prawie na pewno będzie ona inna na devie niż na stagingu czy produkcji. W efekcie hardkodujesz nazwę lokalnego constrainta w skrypcie migracji, który natychmiast się wywala po uruchomieniu na innym środowisku, gdzie ten losowy string nie istnieje. Żeby to naprawić, każdy pojedynczy constraint w twojej bazie danych musi mieć jawną, deterministyczną nazwę. Ręczne robienie tego w setkach modeli jest żmudne i łatwo o tym zapomnieć. Lepszym podejściem jest skonfigurowanie słownika naming convention w twoim obiekcie MetaData w SQLAlchemy. Ten słownik służy jako globalny szablon dla twojej aplikacji. Definiujesz reguły dla każdego typu constrainta. Na przykład możesz określić, że każdy index powinien być nazwany z użyciem prefiksu i x, po którym następuje nazwa tabeli, a następnie nazwa kolumny. Ustawiasz podobne szablony dla unique constraintów, check constraintów i foreign keyów. Następnie podpinasz ten skonfigurowany obiekt MetaData do swojej klasy declarative base. I tu zaczyna się najważniejsze. Kiedy ten słownik jest już na miejscu, Alembic automatycznie integruje twoje naming conventions zarówno ze swoją funkcją autogenerate, jak i operacjami manualnymi. Kiedy odpalasz komendę do autogenerowania nowej migracji, Alembic patrzy na twoje modele, widzi nowy constraint i sprawdza słownik MetaData. Aplikuje twój szablon, kalkuluje jawną nazwę i zapisuje dokładnie ten string w wygenerowanym skrypcie w Pythonie. Ponieważ wygenerowany skrypt jawnie nakazuje bazie danych użycie tej konkretnej nazwy, constraint będzie identyczny na każdym pojedynczym środowisku. Ta integracja rozciąga się też na operacje Alembica odpalane podczas samego procesu upgrade'u. Jeśli skrypt migracji zawiera operację create table albo add column z inline constraintami, którym brakuje jawnych nazw, Alembic nie przekazuje ich po prostu w ciemno do bazy danych. Przechwytuje je, sprawdza szablon naming convention i przypisuje poprawną, deterministyczną nazwę przed wykonaniem komend na bazie danych. Deterministyczna naming convention gwarantuje, że reguła utworzona na twojej lokalnej maszynie będzie miała dokładnie ten sam identyfikator, kiedy dotrze na serwery produkcyjne, całkowicie eliminując ryzyko niemożliwych do zdropowania constraintów. Dzięki za wysłuchanie. Trzymajcie się wszyscy.
5

Migracje offline i generowanie SQL

4m 35s

Sprawdź, jak generować czyste skrypty SQL dla administratorów baz danych, zamiast uruchamiać Pythona bezpośrednio na bazie produkcyjnej. Omawiamy przepływ wykonywania w trybie offline.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Migracje baz danych w Alembic, odcinek 5 z 8. W ściśle regulowanych środowiskach enterprise, developerzy prawie nigdy nie dostają bezpośredniego dostępu, żeby uruchamiać Pythona na produkcyjnych bazach danych. Kiedy nadchodzi dzień deployu, administratorzy baz danych zazwyczaj odcinają ci dostęp i zamiast tego wymagają surowego skryptu gotowego do code review. Wypełnieniem tej luki między twoim codebase'em w Pythonie a ich rygorystycznym procesem deployu zajmują się migracje offline i generowanie SQL-a. Normalnie Alembic łączy się z działającą bazą danych i wykonuje zmiany schematu bezpośrednio przez to połączenie. Ale kiedy musisz przekazać zespołowi DBA zwykły plik tekstowy, używasz trybu offline. Dodając flagę dash dash sql do twoich komend upgrade lub downgrade w terminalu, Alembic całkowicie zmienia swoje zachowanie. Zamiast uruchamiać te instrukcje na silniku bazy danych, renderuje je jako ciągły string standardowego SQL-a i zrzuca bezpośrednio na standard output. Możesz łatwo przekierować ten output z terminala do pliku tekstowego. To podwójne zachowanie to nie magia, jest ono jawnie zdefiniowane w pliku środowiskowym twojego projektu, zazwyczaj nazwanym env dot py. Jeśli zajrzysz do tego pliku, znajdziesz dwie odrębne funkcje routujące. Pierwsza to run migrations online. Ta funkcja tworzy działający silnik bazy danych, binduje aktywne połączenie do kontekstu Alembic i uruchamia twoje skrypty migracyjne krok po kroku. Druga funkcja to run migrations offline, i to właśnie tutaj dzieje się cała translacja. Kiedy przekazujesz flagę sql, Alembic ją wykrywa i zamiast tego odpala tę funkcję offline. Konfiguruje kontekst używając tylko URL-a bazy danych. Nie jest nawiązywane żadne połączenie sieciowe i nie jest tworzona żadna instancja silnika. Następnie bierze twoje struktury migracyjne w Pythonie i generuje dokładne instrukcje CREATE, ALTER lub DROP, opakowuje je w standardowe bloki transakcji BEGIN i COMMIT, i formatuje wszystko pod twój konkretny dialekt bazy danych. I tu jest kluczowa sprawa. Ponieważ tryb offline nigdy tak naprawdę nie łączy się z bazą danych, twoje skrypty migracyjne nie mogą polegać na aktywnym stanie bazy. Nie możesz wykonać instrukcji SELECT wewnątrz migracji offline, żeby sprawdzić, czy dany wiersz istnieje, i nie możesz sprawdzić obecnego stanu tabeli przed wprowadzeniem zmiany. Jeśli twój kod w Pythonie oczekuje, że kursor bazy danych zwróci dane, żeby zdecydować, jaką zmianę schematu wprowadzić, generowanie offline zakończy się błędem. Skrypt musi być czysto deklaratywny. Po prostu mówi Alembicowi, jakie struktury wygenerować. Wyobraź sobie developera, który kończy lokalny feature branch. Odpalił migracje lokalnie w trybie online, żeby zweryfikować, czy wszystko działa na jego testowej bazie danych. Do produkcyjnego release'u, uruchamia komendę upgrade z konkretną rewizją początkową i końcową, dodaje flagę sql i przekierowuje output do pliku tekstowego. Rezultatem jest czysty, sekwencyjny skrypt SQL. Developer przekazuje ten plik zespołowi DBA. Administratorzy mogą go przeczytać, zweryfikować z ich rygorystycznymi politykami bezpieczeństwa i zaaplikować podczas okna serwisowego przy użyciu standardowych narzędzi do administracji bazą danych. Masz również kontrolę nad tym, jak ten offline'owy output jest generowany. Wewnątrz funkcji run migrations offline, wywołanie context configure przyjmuje parametry, które modyfikują wyrenderowany SQL. Częstym wymaganiem jest konwersja zmiennych na literały. Włączając literal binds w konfiguracji, upewniasz się, że wszelkie dane wstawiane podczas migracji zawierają rzeczywiste wartości bezpośrednio w stringu SQL, zamiast wyrzucać generyczne znaczniki parametrów. To gwarantuje, że output jest w pełni samowystarczalnym skryptem gotowym do wykonania. Prawdziwą wartością generowania offline jest przewidywalność; zamienia ono dynamiczne zmiany stanu w Pythonie w statyczny, audytowalny SQL, który każdy pipeline wdrożeniowy czy zespół security może zweryfikować, zanim jakakolwiek tabela zostanie zmodyfikowana. To wszystko w tym odcinku. Dzięki za wysłuchanie i koduj dalej!
6

Migracje batchowe dla SQLite

3m 33s

Zmierz się z wyzwaniem modyfikowania tabel w SQLite, któremu brakuje pełnego wsparcia dla ALTER TABLE. Poznaj przepływ pracy move and copy, wykorzystując operacje batchowe w Alembic.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Migracje baz danych Alembic, odcinek 6 z 8. Tworzysz aplikację lokalną, testujesz migrację i próbujesz usunąć pojedynczą kolumnę. Baza danych rzuca błędem, a ty odkrywasz zaskakujący fakt: proste polecenie drop column zasadniczo nie istnieje w twoim silniku bazy danych. Taka jest rzeczywistość pracy z SQLite i właśnie dlatego Alembic oferuje Batch Migrations. SQLite ma lekką architekturę z bardzo ograniczonym wsparciem dla modyfikacji istniejących tabel. Możesz dodać kolumnę do tabeli, ale jeśli chcesz usunąć kolumnę, zmienić jej typ lub nazwę, silnik bazy danych po prostu tego nie obsługuje. Wielu programistów napotyka ten problem, próbując wykonać standardową operację drop column w swoim skrypcie Alembic. Działa to idealnie na PostgreSQL, ale na SQLite po prostu crashuje. Alembic rozwiązuje to ograniczenie, stosując wzorzec o nazwie move and copy workflow. Ponieważ baza danych nie może modyfikować struktury tabeli in-place, Alembic odbudowuje całą tabelę od zera pod spodem. Aby skorzystać z tej funkcji, nie wywołujesz bezpośrednio standardowych metod operacji. Zamiast tego używasz context managera o nazwie batch alter table. Przekazujesz nazwę swojej tabeli do tego context managera, a następnie definiujesz wszystkie zmiany strukturalne wewnątrz tego bloku. Kiedy blok kończy się wykonywać, Alembic przejmuje kontrolę i orkiestruje podmianę tabeli. Przyjrzyjmy się konkretnemu scenariuszowi. Masz tabelę o nazwie user_data i musisz usunąć kolumnę o nazwie bar. W swoim skrypcie otwierasz context managera batch alter table dla tabeli user_data. Wewnątrz bloku instruujesz go, aby wykonał drop kolumny o nazwie bar. To cały kod w Pythonie, który piszesz. W momencie wyjścia z context managera, Alembic generuje sekwencję precyzyjnych poleceń SQL, aby wykonać move and copy workflow. Najpierw Alembic odczytuje aktualną strukturę twojej tabeli. Generuje instrukcję create table dla zupełnie nowej, tymczasowej tabeli. Ta nowa tabela ma dokładnie ten sam schemat co oryginalna, z wyjątkiem brakującej kolumny bar. Następnie Alembic kopiuje twoje dane. Uruchamia instrukcję insert, która pobiera wszystkie istniejące wiersze z oryginalnej tabeli i wrzuca je do nowej tabeli tymczasowej. Ponieważ kolumna bar już nie istnieje w nowym schemacie, te konkretne dane zostają po prostu pominięte. Kiedy dane są już bezpiecznie skopiowane, Alembic całkowicie dropuje oryginalną tabelę user_data. Na koniec zmienia nazwę tabeli tymczasowej z powrotem na user_data. Baza danych ląduje w dokładnie takim stanie, jakiego chciałeś, a twoja aplikacja nawet nie wie, że tabela została całkowicie przebudowana. Oto kluczowa kwestia. Context manager batch alter table batchuje twoje operacje razem dla lepszej wydajności. Jeśli musisz usunąć dwie kolumny, dodać nową i zmienić typ danych, umieszczasz wszystkie te instrukcje wewnątrz tego samego bloku kontekstowego. Alembic skompiluje wszystkie te zmiany i wykona move and copy workflow dokładnie jeden raz. Przebudowa dużej tabeli to kosztowna operacja, wymagająca intensywnych odczytów i zapisów na dysku, dlatego zrobienie tego w jednym przebiegu jest kluczowe. Operacje batchowe zamieniają poważne ograniczenie silnika w całkowicie niewidoczny szczegół implementacji, pozwalając ci pisać czyste, database-agnostic skrypty migracyjne, podczas gdy Alembic bezpiecznie odwala całą ciężką robotę związaną z odtwarzaniem tabel w tle. To wszystko w tym odcinku. Dzięki za wysłuchanie i buduj dalej!
7

Praca z gałęziami

4m 01s

Opanuj współpracę w zespole poprzez obsługę rozgałęzionych strumieni migracji. Dowiedz się, jak identyfikować i scalać rozbieżne historie rewizji, gdy wielu programistów modyfikuje bazę danych.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Migracje bazy danych w Alembic, odcinek 7 z 8. Zepsuty merge kodu bywa irytujący, ale zazwyczaj po prostu wywala lokalne testy. Z drugiej strony, dwa konfliktujące schematy bazy danych mogą całkowicie zatrzymać twój deployment pipeline. Robisz merge kodu do main, ale nagle twoje narzędzie do migracji bazy danych rzuca błędem o multiple heads. Taka jest rzeczywistość pracy z branchami w Alembic, a rozwiązanie tego problemu wymaga zrozumienia, jak rozwidla się oś czasu twojej bazy danych. Branche powstają naturalnie w każdym środowisku zespołowym. Programista A pracuje nad nowym featurem i generuje skrypt migracji, żeby dodać tabelę koszyka. Ten nowy skrypt wskazuje na obecny stan bazy danych, nazwijmy go rewizją 100, jako swoją bazę. W międzyczasie programista B pracuje na innym branchu i generuje skrypt dodający kolumnę konta. Jego skrypt również wskazuje na rewizję 100 jako swoją bazę. Obaj programiści testują lokalnie, wszystko działa poprawnie, a oba pull requesty zostają zmergowane do głównego repozytorium. Masz teraz w projekcie dwa oddzielne skrypty migracji, z których oba twierdzą, że są bezpośrednimi następcami rewizji 100. Oś czasu migracji rozwidliła się na dwie równoległe ścieżki. Jeśli odpalisz komendę upgrade, żeby zaktualizować bazę danych do rewizji head, Alembic natychmiast się zatrzyma. Rzuci błędem informującym, że istnieją multiple heads. Narzędzie odmawia zgadywania, która migracja powinna zostać zaaplikowana jako pierwsza, ponieważ wprowadzanie zmian w bazie danych w nieprzewidywalnej kolejności jest niebezpieczne. Aby to rozwiązać, musisz połączyć te rozbieżne ścieżki używając komendy merge w Alembic. I tu pojawia się kluczowa kwestia. Merge w Alembic to nie to samo co merge w Gicie. Nie zagląda on do plików Pythona i nie próbuje automatycznie zmergować zmian w schemacie do jednego pliku. Zamiast tego, komenda merge tworzy zupełnie nowy, pusty skrypt migracji. Ten nowy skrypt nie zawiera żadnych operacji na bazie danych. Nie modyfikuje tabel ani nie dodaje kolumn. Jego cel jest czysto strukturalny. W wygenerowanym pliku Pythona, zmienna down revision jest ustawiona na tuple zawierającą identyfikatory rewizji obu rozbieżnych skryptów, a nie na pojedynczy string. Ta jedna akcja z powrotem łączy ze sobą dwa równoległe branche. Tworzy nowy, ujednolicony head dla osi czasu. Kiedy odpalasz tę komendę, zazwyczaj przekazujesz jej słowo heads, co mówi Alembicowi, żeby znalazł wszystkie obecne endpointy w twojej historii migracji i je zmergował. Możesz też dołączyć string z wiadomością, żeby udokumentować synchronizację, podobnie jak w commit message. Kiedy ten skrypt merge zostanie wygenerowany i zacommitowany do twojego repozytorium, twoja oś czasu znów staje się liniowa. Następnym razem, gdy odpalisz komendę upgrade, Alembic wykona oba skrypty nadrzędne w bezpiecznej sekwencji, a następnie ostempluje bazę danych nowym, zmergowanym ID rewizji. Strukturalna integralność historii twojej bazy danych zależy od tej synchronizacji. Branch w Alembic to po prostu fork w twojej historii migracji, a jego naprawa oznacza wygenerowanie dedykowanego skryptu, który działa jak fizyczny węzeł wiążący te rozbieżne ścieżki z powrotem w jedną, przejrzystą sekwencję. Jeśli uważasz te odcinki za pomocne i chcesz wesprzeć program, możesz wyszukać DevStoriesEU na Patreon. To wszystko w tym odcinku. Dzięki za słuchanie i koduj dalej!
8

Produkcyjne usprawnienia

4m 15s

Poszerz swoją wiedzę o Alembic dzięki zaawansowanym technikom. Omawiamy programistyczne wywoływanie poleceń i współdzielenie połączenia z frameworkami aplikacyjnymi, takimi jak FastAPI.

Pobierz
Cześć, tu Alex z DEV STORIES DOT EU. Migracje baz danych w Alembic, odcinek 8 z 8. W nowoczesnych, konteneryzowanych aplikacjach poleganie na ręcznych skryptach z command-line'a do przygotowania bazy danych przed startem aplikacji to operacyjny ból głowy. Twoja aplikacja powinna być na tyle mądra, żeby sama weryfikować i aktualizować swój schemat podczas startu. Żeby zrobić to bezpiecznie, potrzebujesz produkcyjnych Power-Upów. Większość developerów zna Alembic tylko jako narzędzie terminalowe. Wpisujesz komendę upgrade, a ona modyfikuje bazę danych. Ale interfejs command-line to tylko cienki wrapper. Pod spodem kryje się programistyczne API Alembica. Możesz odpalać migracje bezpośrednio z kodu twojej aplikacji w Pythonie. Pozwala to nowoczesnym frameworkom backendowym na automatyczne wykonywanie aktualizacji schematu podczas ich rutyny startowej, zapewniając, że kod i baza danych są zawsze idealnie zsynchronizowane. Jednak zrobienie tego naiwnie wprowadza pewien subtelny problem. Kiedy odpalasz migrację programistycznie, Alembic domyślnie stosuje swoje standardowe zachowanie. Czyta twój plik konfiguracyjny, tworzy zupełnie nowy engine bazy danych, otwiera połączenie, odpala migrację i ją zamyka. Ale twoja aplikacja dopiero co wystartowała. Zdążyła już utworzyć engine i zainicjować connection pool. Pozwolenie Alembicowi na odpalenie całkowicie oddzielnego połączenia podczas startu jest nieefektywne. Co ważniejsze, może to być niebezpieczne. Jeśli sekwencja startowa twojej aplikacji trzyma locka na tabeli, oddzielne połączenie Alembica zawiesi się w oczekiwaniu na tego locka, powodując deadlock, który wywali twój kontener. To także poważny problem, jeśli odpalasz automatyczne testy na bazie in-memory. W takim scenariuszu zupełnie nowe połączenie wskazuje na całkowicie pustą bazę, co oznacza, że twoje migracje nie zaaplikują się do danych, które faktycznie testujesz. Rozwiązujesz ten problem, przekazując aktywne połączenie do bazy danych twojej aplikacji bezpośrednio do Alembica. Robi się to za pomocą obiektu konfiguracyjnego Alembica. Najpierw kod twojej aplikacji tworzy instancję obiektu konfiguracyjnego, wskazując mu twój główny plik inicjalizacyjny. I tu jest kluczowa sprawa. Obiekt konfiguracyjny posiada słownik attributes. Działa on jak most do przekazywania żywych obiektów Pythona do środowiska migracji. Pobierasz aktywne połączenie z engine'u twojej aplikacji i przypisujesz je do klucza o nazwie connection wewnątrz tego słownika attributes. Następnie wywołujesz programistyczne API Alembica, a konkretnie komendę upgrade, przekazując jej twój zmodyfikowany obiekt konfiguracyjny i każąc jej zrobić upgrade do head revision. Ale Alembic nie wie automatycznie, co zrobić z tym wstrzykniętym połączeniem. Musisz zmodyfikować swój plik środowiska migracji, żeby zamknąć obwód. W sekcji twojego pliku środowiska, która obsługuje migracje online, dodajesz prosty check przed wykonaniem setupu. Instruujesz skrypt, żeby zajrzał do attributes konfiguracji. Jeśli znajdzie tam obiekt połączenia, pomija tworzenie nowego engine'u. Zamiast tego, konfiguruje context migracji tak, aby używał połączenia, które mu dostarczyłeś. Jeśli nie znajdzie połączenia w attributes, bezpiecznie robi fallback do normalnego zachowania, tworząc nowy engine z URL-a bazy danych. Ten fallback gwarantuje, że twoje narzędzia command-line nadal będą działać dokładnie tak samo jak wcześniej, gdy odpalasz je lokalnie. Projektując swój system w ten sposób, zmieniasz migracje z zewnętrznego obowiązku podczas deployu w natywną, przewidywalną część cyklu życia aplikacji. Kiedy nowa instancja startuje, prosi o połączenie, ogarnia własne upgrade'y w ramach tej sesji i płynnie przechodzi do obsługi ruchu. I to by było na tyle w naszej serii o migracjach baz danych. Gorąco zachęcam cię do przejrzenia oficjalnej dokumentacji Alembica i wypróbowania tych programistycznych konfiguracji w praktyce. Jeśli masz pomysły na zupełnie nowe tematy, o których chciałbyś usłyszeć, wejdź na devstories dot eu i daj nam znać. To wszystko w tym odcinku. Dzięki za wysłuchanie i buduj dalej!