Zarządzanie migracjami baz danych to duże wyzwanie w każdym projekcie oprogramowania. Na szczęście od wersji 1.7 Django zawiera wbudowaną platformę migracji. Framework jest bardzo wydajny i przydatny w zarządzaniu zmianami w bazach danych. Jednak elastyczność zapewniona przez ramy wymagała pewnych kompromisów. Aby zrozumieć ograniczenia migracji do Django, zajmiesz się dobrze znanym problemem:tworzeniem indeksu w Django bez przestojów.
Z tego samouczka dowiesz się:
- Jak i kiedy Django generuje nowe migracje
- Jak sprawdzić polecenia generowane przez Django w celu wykonania migracji
- Jak bezpiecznie modyfikować migracje, aby dopasować je do swoich potrzeb
Ten samouczek na poziomie średniozaawansowanym jest przeznaczony dla czytelników, którzy są już zaznajomieni z migracjami Django. Aby uzyskać wprowadzenie do tego tematu, sprawdź Django Migrations:A Primer.
Bezpłatny bonus: Kliknij tutaj, aby uzyskać bezpłatny dostęp do dodatkowych samouczków i zasobów Django, których możesz użyć, aby pogłębić swoje umiejętności tworzenia stron internetowych w Pythonie.
Problem z tworzeniem indeksu w migracjach Django
Częstą zmianą, która zwykle staje się konieczna, gdy dane przechowywane przez aplikację rosną, jest dodanie indeksu. Indeksy służą do przyspieszania zapytań i sprawiają, że Twoja aplikacja jest szybka i responsywna.
W większości baz danych dodanie indeksu wymaga wyłącznej blokady tabeli. Blokada na wyłączność uniemożliwia operacje modyfikacji danych (DML), takie jak UPDATE
, INSERT
i DELETE
, podczas gdy indeks jest tworzony.
Blokady są uzyskiwane niejawnie przez bazę danych podczas wykonywania pewnych operacji. Na przykład, gdy użytkownik zaloguje się do Twojej aplikacji, Django zaktualizuje last_login
pole w auth_user
stół. Aby przeprowadzić aktualizację, baza danych będzie musiała najpierw uzyskać blokadę wiersza. Jeśli wiersz jest aktualnie blokowany przez inne połączenie, możesz otrzymać wyjątek bazy danych.
Zablokowanie tabeli może stanowić problem, gdy konieczne jest utrzymanie dostępności systemu podczas migracji. Im większa tabela, tym dłużej może zająć utworzenie indeksu. Im dłużej trwa tworzenie indeksu, tym dłużej system jest niedostępny lub nie odpowiada użytkownikom.
Niektórzy dostawcy baz danych umożliwiają tworzenie indeksu bez blokowania tabeli. Na przykład, aby utworzyć indeks w PostgreSQL bez blokowania tabeli, możesz użyć CONCURRENTLY
słowo kluczowe:
CREATE INDEX CONCURRENTLY ix ON table (column);
W Oracle istnieje ONLINE
opcja zezwalająca na operacje DML na tabeli podczas tworzenia indeksu:
CREATE INDEX ix ON table (column) ONLINE;
Podczas generowania migracji Django nie użyje tych specjalnych słów kluczowych. Uruchomienie migracji w takiej postaci spowoduje, że baza danych uzyska blokadę na wyłączność w tabeli i uniemożliwi operacje DML podczas tworzenia indeksu.
Równoczesne tworzenie indeksu ma pewne zastrzeżenia. Ważne jest, aby wcześniej zrozumieć problemy specyficzne dla zaplecza bazy danych. Na przykład jednym zastrzeżeniem w PostgreSQL jest to, że jednoczesne tworzenie indeksu trwa dłużej, ponieważ wymaga dodatkowego skanowania tabeli.
W tym samouczku użyjesz migracji Django do utworzenia indeksu na dużej tabeli bez powodowania przestojów.
Uwaga: Aby skorzystać z tego samouczka, zaleca się korzystanie z backendu PostgreSQL, Django 2.x i Pythona 3.
Możliwe jest również śledzenie wraz z innymi backendami baz danych. W miejscach, w których używane są funkcje SQL unikalne dla PostgreSQL, zmień SQL, aby pasował do backendu Twojej bazy danych.
Konfiguracja
Będziesz korzystać z wymyślonej Sale
model w aplikacji o nazwie app
. W rzeczywistej sytuacji modele takie jak Sale
są głównymi tabelami w bazie danych i zazwyczaj są bardzo duże i przechowują dużo danych:
# models.py
from django.db import models
class Sale(models.Model):
sold_at = models.DateTimeField(
auto_now_add=True,
)
charged_amount = models.PositiveIntegerField()
Aby utworzyć tabelę, wygeneruj początkową migrację i zastosuj ją:
$ python manage.py makemigrations
Migrations for 'app':
app/migrations/0001_initial.py
- Create model Sale
$ python manage migrate
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0001_initial... OK
Po pewnym czasie tabela sprzedaży staje się bardzo duża, a użytkownicy zaczynają narzekać na powolność. Podczas monitorowania bazy danych zauważyłeś, że wiele zapytań używa sold_at
kolumna. Aby przyspieszyć działanie, decydujesz, że potrzebujesz indeksu w kolumnie.
Aby dodać indeks na sold_at
, wprowadzasz następującą zmianę w modelu:
# models.py
from django.db import models
class Sale(models.Model):
sold_at = models.DateTimeField(
auto_now_add=True,
db_index=True,
)
charged_amount = models.PositiveIntegerField()
Jeśli uruchomisz tę migrację w takiej postaci, w jakiej jest, Django utworzy indeks w tabeli i będzie on zablokowany do czasu ukończenia indeksu. Utworzenie indeksu na bardzo dużym stole może zająć trochę czasu, a chcesz uniknąć przestojów.
W lokalnym środowisku programistycznym z małym zestawem danych i bardzo małą liczbą połączeń ta migracja może wydawać się natychmiastowa. Jednak w przypadku dużych zbiorów danych z wieloma równoczesnymi połączeniami uzyskanie blokady i utworzenie indeksu może chwilę potrwać.
W kolejnych krokach zmodyfikujesz migracje utworzone przez Django, aby utworzyć indeks bez powodowania przestojów.
Fałszywa migracja
Pierwszym podejściem jest ręczne utworzenie indeksu. Zamierzasz wygenerować migrację, ale nie pozwolisz Django jej zastosować. Zamiast tego uruchomisz SQL ręcznie w bazie danych, a następnie sprawisz, że Django pomyśli, że migracja została zakończona.
Najpierw wygeneruj migrację:
$ python manage.py makemigrations --name add_index_fake
Migrations for 'app':
app/migrations/0002_add_index_fake.py
- Alter field sold_at on sale
Użyj sqlmigrate
polecenie, aby wyświetlić SQL, którego Django użyje do wykonania tej migracji:
$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;
Chcesz utworzyć indeks bez blokowania tabeli, więc musisz zmodyfikować polecenie. Dodaj CONCURRENTLY
słowo kluczowe i wykonaj w bazie danych:
app=# CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");
CREATE INDEX
Zauważ, że wykonałeś polecenie bez BEGIN
i COMMIT
Części. Pominięcie tych słów kluczowych spowoduje wykonanie poleceń bez transakcji w bazie danych. Transakcje z bazami danych omówimy w dalszej części artykułu.
Po wykonaniu polecenia, jeśli spróbujesz zastosować migracje, pojawi się następujący błąd:
$ python manage.py migrate
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0002_add_index_fake...Traceback (most recent call last):
File "venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 85, in _execute
return self.cursor.execute(sql, params)
psycopg2.ProgrammingError: relation "app_sale_sold_at_b9438ae4" already exists
Django skarży się, że indeks już istnieje, więc nie może kontynuować migracji. Właśnie utworzyłeś indeks bezpośrednio w bazie danych, więc teraz musisz sprawić, by Django pomyślał, że migracja została już zastosowana.
Jak sfałszować migrację
Django zapewnia wbudowany sposób oznaczania migracji jako wykonanych, bez faktycznego ich wykonywania. Aby użyć tej opcji, ustaw --fake
flaga podczas stosowania migracji:
$ python manage.py migrate --fake
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0002_add_index_fake... FAKED
Django tym razem nie zgłosiło błędu. W rzeczywistości Django tak naprawdę nie zastosowało żadnej migracji. Po prostu oznaczył go jako wykonany (lub FAKED
).
Oto kilka kwestii, które należy wziąć pod uwagę podczas sfałszowania migracji:
-
Polecenie ręczne musi być równoważne SQL wygenerowanemu przez Django: Musisz upewnić się, że wykonywane polecenie jest równoważne z kodem SQL generowanym przez Django. Użyj
sqlmigrate
aby utworzyć polecenie SQL. Jeśli polecenia nie pasują, możesz skończyć z niespójnością między bazą danych a stanem modeli. -
Inne niezastosowane migracje również zostaną sfałszowane: Jeśli masz wiele niezastosowanych migracji, wszystkie zostaną sfałszowane. Przed zastosowaniem migracji ważne jest, aby upewnić się, że tylko te migracje, które chcesz sfałszować, nie zostały zastosowane. W przeciwnym razie możesz skończyć z niespójnościami. Inną opcją jest określenie dokładnej migracji, którą chcesz sfałszować.
-
Wymagany jest bezpośredni dostęp do bazy danych: Musisz uruchomić polecenie SQL w bazie danych. Nie zawsze jest to możliwe. Ponadto wykonywanie poleceń bezpośrednio w produkcyjnej bazie danych jest niebezpieczne i powinno się go unikać, jeśli to możliwe.
-
Zautomatyzowane procesy wdrażania mogą wymagać korekt: Jeśli zautomatyzowałeś proces wdrażania (przy użyciu CI, CD lub innych narzędzi do automatyzacji), być może będziesz musiał zmienić proces, aby sfałszować migracje. Nie zawsze jest to pożądane.
Oczyszczanie
Zanim przejdziesz do następnej sekcji, musisz przywrócić bazę danych do stanu zaraz po początkowej migracji. Aby to zrobić, wróć do początkowej migracji:
$ python manage.py migrate 0001
Operations to perform:
Target specific migration: 0001_initial, from app
Running migrations:
Rendering model states... DONE
Unapplying app.0002_add_index_fake... OK
Django cofnęło zmiany wprowadzone w drugiej migracji, więc teraz można również bezpiecznie usunąć plik:
$ rm app/migrations/0002_add_index_fake.py
Aby upewnić się, że zrobiłeś wszystko dobrze, sprawdź migracje:
$ python manage.py showmigrations app
app
[X] 0001_initial
Początkowa migracja została zastosowana i nie ma niezastosowanych migracji.
Wykonywanie surowego SQL w migracjach
W poprzedniej sekcji wykonałeś SQL bezpośrednio w bazie danych i sfałszowałeś migrację. To załatwia sprawę, ale jest lepsze rozwiązanie.
Django umożliwia wykonanie surowego SQL w migracjach przy użyciu RunSQL
. Spróbujmy go użyć zamiast wykonywać polecenie bezpośrednio w bazie danych.
Najpierw wygeneruj nową pustą migrację:
$ python manage.py makemigrations app --empty --name add_index_runsql
Migrations for 'app':
app/migrations/0002_add_index_runsql.py
Następnie edytuj plik migracji i dodaj RunSQL
operacja:
# migrations/0002_add_index_runsql.py
from django.db import migrations, models
class Migration(migrations.Migration):
atomic = False
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.RunSQL(
'CREATE INDEX "app_sale_sold_at_b9438ae4" '
'ON "app_sale" ("sold_at");',
),
]
Po uruchomieniu migracji otrzymasz następujące dane wyjściowe:
$ python manage.py migrate
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0002_add_index_runsql... OK
To wygląda dobrze, ale jest problem. Spróbujmy ponownie wygenerować migracje:
$ python manage.py makemigrations --name leftover_migration
Migrations for 'app':
app/migrations/0003_leftover_migration.py
- Alter field sold_at on sale
Django ponownie wygenerowało tę samą migrację. Dlaczego to zrobiło?
Oczyszczanie
Zanim odpowiemy na to pytanie, musisz wyczyścić i cofnąć zmiany wprowadzone w bazie danych. Zacznij od usunięcia ostatniej migracji. Nie został zastosowany, więc można bezpiecznie usunąć:
$ rm app/migrations/0003_leftover_migration.py
Następnie wypisz migracje dla app
aplikacja:
$ python manage.py showmigrations app
app
[X] 0001_initial
[X] 0002_add_index_runsql
Trzecia migracja zniknęła, ale zastosowana została druga. Chcesz wrócić do stanu zaraz po początkowej migracji. Spróbuj przeprowadzić migrację z powrotem do początkowej migracji, tak jak w poprzedniej sekcji:
$ python manage.py migrate app 0001
Operations to perform:
Target specific migration: 0001_initial, from app
Running migrations:
Rendering model states... DONE
Unapplying app.0002_add_index_runsql...Traceback (most recent call last):
NotImplementedError: You cannot reverse this operation
Django nie może cofnąć migracji.
Operacja odwrotnej migracji
Aby odwrócić migrację, Django wykonuje przeciwną akcję dla każdej operacji. W tym przypadku odwrotnością dodawania indeksu jest jego porzucenie. Jak już zauważyłeś, gdy migracja jest odwracalna, możesz ją cofnąć. Tak jak możesz użyć checkout
w Git możesz cofnąć migrację, jeśli wykonasz migrate
do wcześniejszej migracji.
Wiele wbudowanych operacji migracji już definiuje akcję odwrotną. Na przykład odwrotną akcją związaną z dodawaniem pola jest usunięcie odpowiedniej kolumny. Odwrotną czynnością przy tworzeniu modelu jest upuszczenie odpowiedniej tabeli.
Niektóre operacje migracji nie są odwracalne. Na przykład nie ma odwrotnej akcji usuwania pola lub usuwania modelu, ponieważ po zastosowaniu migracji dane znikają.
W poprzedniej sekcji użyłeś RunSQL
operacja. Podczas próby cofnięcia migracji wystąpił błąd. Zgodnie z błędem, jednej z operacji w migracji nie można cofnąć. Django domyślnie nie może odwrócić surowego SQL. Ponieważ Django nie wie, co zostało wykonane przez operację, nie może automatycznie wygenerować przeciwnej akcji.
Jak sprawić, by migracja była odwracalna
Aby migracja była odwracalna, wszystkie operacje w niej zawarte muszą być odwracalne. Nie można cofnąć części migracji, więc pojedyncza nieodwracalna operacja sprawi, że cała migracja będzie nieodwracalna.
Aby utworzyć RunSQL
operacja odwracalna, należy podać kod SQL do wykonania, gdy operacja zostanie odwrócona. Odwrotny SQL jest dostępny w reverse_sql
argument.
Działaniem odwrotnym do dodawania indeksu jest jego porzucenie. Aby migracja była odwracalna, podaj reverse_sql
aby usunąć indeks:
# migrations/0002_add_index_runsql.py
from django.db import migrations, models
class Migration(migrations.Migration):
atomic = False
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.RunSQL(
'CREATE INDEX "app_sale_sold_at_b9438ae4" '
'ON "app_sale" ("sold_at");',
reverse_sql='DROP INDEX "app_sale_sold_at_b9438ae4";',
),
]
Teraz spróbuj odwrócić migrację:
$ python manage.py showmigrations app
app
[X] 0001_initial
[X] 0002_add_index_runsql
$ python manage.py migrate app 0001
Operations to perform:
Target specific migration: 0001_initial, from app
Running migrations:
Rendering model states... DONE
Unapplying app.0002_add_index_runsql... OK
$ python manage.py showmigrations app
app
[X] 0001_initial
[ ] 0002_add_index_runsql
Druga migracja została odwrócona, a indeks został usunięty przez Django. Teraz możesz bezpiecznie usunąć plik migracji:
$ rm app/migrations/0002_add_index_runsql.py
Zawsze dobrze jest podać reverse_sql
. W sytuacjach, w których odwrócenie surowej operacji SQL nie wymaga żadnych działań, można oznaczyć operację jako odwracalną za pomocą specjalnego warunku migrations.RunSQL.noop
:
migrations.RunSQL(
sql='...', # Your forward SQL here
reverse_sql=migrations.RunSQL.noop,
),
Zrozumienie stanu modelu i stanu bazy danych
W poprzedniej próbie ręcznego utworzenia indeksu przy użyciu RunSQL
, Django wielokrotnie generowało tę samą migrację, mimo że indeks został utworzony w bazie danych. Aby zrozumieć, dlaczego Django to zrobił, najpierw musisz zrozumieć, w jaki sposób Django decyduje, kiedy generować nowe migracje.
Kiedy Django generuje nową migrację
W procesie generowania i stosowania migracji Django synchronizuje stan bazy danych ze stanem modeli. Na przykład, kiedy dodajesz pole do modelu, Django dodaje kolumnę do tabeli. Kiedy usuwasz pole z modelu, Django usuwa kolumnę z tabeli.
Aby zsynchronizować modele i bazę danych, Django utrzymuje stan reprezentujący modele. Aby zsynchronizować bazę danych z modelami, Django generuje operacje migracji. Operacje migracji przekładają się na specyficzny dla dostawcy kod SQL, który można wykonać w bazie danych. Po wykonaniu wszystkich operacji migracji oczekuje się, że baza danych i modele będą spójne.
Aby uzyskać stan bazy danych, Django agreguje operacje ze wszystkich poprzednich migracji. Gdy zagregowany stan migracji nie jest zgodny ze stanem modeli, Django generuje nową migrację.
W poprzednim przykładzie utworzyłeś indeks za pomocą surowego kodu SQL. Django nie wiedziało, że utworzyłeś indeks, ponieważ nie użyłeś znanej operacji migracji.
Kiedy Django zagregowało wszystkie migracje i porównało je ze stanem modeli, okazało się, że brakuje indeksu. Dlatego nawet po ręcznym utworzeniu indeksu Django nadal myślał, że go nie ma i wygenerował dla niego nową migrację.
Jak oddzielić bazę danych i stan w migracjach
Ponieważ Django nie jest w stanie utworzyć indeksu w sposób, w jaki chcesz, chcesz dostarczyć własny kod SQL, ale nadal poinformuj Django, że go utworzyłeś.
Innymi słowy, musisz wykonać coś w bazie danych i zapewnić Django operację migracji, aby zsynchronizować jego stan wewnętrzny. W tym celu Django udostępnia nam specjalną operację migracji o nazwie SeparateDatabaseAndState
. Ta operacja nie jest dobrze znana i powinna być zarezerwowana dla specjalnych przypadków, takich jak ten.
O wiele łatwiej jest edytować migracje niż pisać je od zera, więc zacznij od wygenerowania migracji w zwykły sposób:
$ python manage.py makemigrations --name add_index_separate_database_and_state
Migrations for 'app':
app/migrations/0002_add_index_separate_database_and_state.py
- Alter field sold_at on sale
To jest zawartość migracji wygenerowana przez Django, taka sama jak poprzednio:
# migrations/0002_add_index_separate_database_and_state.py
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='sale',
name='sold_at',
field=models.DateTimeField(
auto_now_add=True,
db_index=True,
),
),
]
Django wygenerowało AlterField
operacja na polu sold_at
. Operacja utworzy indeks i zaktualizuje stan. Chcemy zachować tę operację, ale udostępnić inne polecenie do wykonania w bazie danych.
Jeszcze raz, aby uzyskać polecenie, użyj kodu SQL wygenerowanego przez Django:
$ python manage.py sqlmigrate app 0002
BEGIN;
--
-- Alter field sold_at on sale
--
CREATE INDEX "app_sale_sold_at_b9438ae4" ON "app_sale" ("sold_at");
COMMIT;
Dodaj CONCURRENTLY
słowo kluczowe w odpowiednim miejscu:
CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");
Następnie edytuj plik migracji i użyj SeparateDatabaseAndState
aby udostępnić zmodyfikowane polecenie SQL do wykonania:
# migrations/0002_add_index_separate_database_and_state.py
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name='sale',
name='sold_at',
field=models.DateTimeField(
auto_now_add=True,
db_index=True,
),
),
],
database_operations=[
migrations.RunSQL(sql="""
CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");
""", reverse_sql="""
DROP INDEX "app_sale_sold_at_b9438ae4";
"""),
],
),
],
Operacja migracji SeparateDatabaseAndState
akceptuje 2 listy operacji:
- state_operations to operacje do zastosowania na stanie modelu wewnętrznego. Nie wpływają na bazę danych.
- operacje_bazy_danych to operacje do zastosowania w bazie danych.
Zachowałeś oryginalną operację wygenerowaną przez Django w state_operations
. Podczas korzystania z SeparateDatabaseAndState
, to jest to, co zwykle będziesz chciał zrobić. Zauważ, że db_index=True
argument jest dostarczany do pola. Ta operacja migracji poinformuje Django, że w polu znajduje się indeks.
Użyłeś kodu SQL wygenerowanego przez Django i dodałeś CONCURRENTLY
słowo kluczowe. Użyłeś specjalnej akcji RunSQL
do wykonania surowego SQL podczas migracji.
Jeśli spróbujesz przeprowadzić migrację, otrzymasz następujące dane wyjściowe:
$ python manage.py migrate app
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0002_add_index_separate_database_and_state...Traceback (most recent call last):
File "/venv/lib/python3.7/site-packages/django/db/backends/utils.py", line 83, in _execute
return self.cursor.execute(sql)
psycopg2.InternalError: CREATE INDEX CONCURRENTLY cannot run inside a transaction block
Migracje nieatomowe
W SQL CREATE
, DROP
, ALTER
i TRUNCATE
operacje są określane jako język definicji danych (DDL). W bazach danych obsługujących transakcyjne DDL, takich jak PostgreSQL, Django domyślnie wykonuje migracje wewnątrz transakcji bazy danych. Jednakże, zgodnie z powyższym błędem, PostgreSQL nie może jednocześnie tworzyć indeksu wewnątrz bloku transakcji.
Aby móc jednocześnie tworzyć indeks w ramach migracji, musisz powiedzieć Django, aby nie wykonywał migracji w transakcji bazy danych. Aby to zrobić, oznacz migrację jako nieatomową, ustawiając atomic
na False
:
# migrations/0002_add_index_separate_database_and_state.py
from django.db import migrations, models
class Migration(migrations.Migration):
atomic = False
dependencies = [
('app', '0001_initial'),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name='sale',
name='sold_at',
field=models.DateTimeField(
auto_now_add=True,
db_index=True,
),
),
],
database_operations=[
migrations.RunSQL(sql="""
CREATE INDEX CONCURRENTLY "app_sale_sold_at_b9438ae4"
ON "app_sale" ("sold_at");
""",
reverse_sql="""
DROP INDEX "app_sale_sold_at_b9438ae4";
"""),
],
),
],
Po oznaczeniu migracji jako nieatomowej możesz uruchomić migrację:
$ python manage.py migrate app
Operations to perform:
Apply all migrations: app
Running migrations:
Applying app.0002_add_index_separate_database_and_state... OK
Właśnie wykonałeś migrację bez powodowania przestojów.
Oto kilka kwestii do rozważenia podczas korzystania z SeparateDatabaseAndState
:
-
Operacje na bazie danych muszą być równoważne operacjom stanu: Niespójności między stanem bazy danych a stanem modelu mogą przysporzyć wielu kłopotów. Dobrym punktem wyjścia jest trzymanie operacji generowanych przez Django w
state_operations
i edytuj wyjściesqlmigrate
do użycia wdatabase_operations
. -
Migracje nieatomowe nie mogą zostać wycofane w przypadku błędu: Jeśli podczas migracji wystąpi błąd, nie będzie można wycofać. Będziesz musiał wycofać migrację lub ukończyć ją ręcznie. Dobrym pomysłem jest ograniczenie do minimum operacji wykonywanych wewnątrz migracji nieatomowej. Jeśli masz dodatkowe operacje w migracji, przenieś je do nowej migracji.
-
Migracja może zależeć od dostawcy: SQL generowany przez Django jest specyficzny dla backendu bazy danych używanego w projekcie. Może działać z innymi backendami baz danych, ale nie jest to gwarantowane. Jeśli potrzebujesz obsługiwać wiele backendów baz danych, musisz wprowadzić pewne poprawki w tym podejściu.
Wniosek
Zacząłeś ten samouczek z dużą tabelą i problemem. Chciałeś, aby Twoja aplikacja była szybsza dla użytkowników i chciałeś to zrobić bez powodowania przestojów.
Pod koniec samouczka udało Ci się wygenerować i bezpiecznie zmodyfikować migrację Django, aby osiągnąć ten cel. Po drodze zmierzyłeś się z różnymi problemami i udało Ci się je rozwiązać za pomocą wbudowanych narzędzi dostarczonych przez platformę migracji.
W tym samouczku nauczyłeś się następujących rzeczy:
- Jak migracje Django działają wewnętrznie przy użyciu modelu i stanu bazy danych oraz kiedy generowane są nowe migracje
- Jak wykonać niestandardowy SQL w migracjach przy użyciu
RunSQL
akcja - Co to są migracje odwracalne i jak utworzyć
RunSQL
działanie odwracalne - Co to są migracje atomowe i jak zmienić domyślne zachowanie zgodnie z własnymi potrzebami
- Jak bezpiecznie wykonywać złożone migracje w Django
Ważną koncepcją jest oddzielenie stanu modelu od stanu bazy danych. Gdy zrozumiesz to i jak z niego korzystać, możesz przezwyciężyć wiele ograniczeń wbudowanych operacji migracji. Niektóre przypadki użycia, które przychodzą na myśl, obejmują dodanie indeksu, który został już utworzony w bazie danych i podanie argumentów specyficznych dla dostawcy do poleceń DDL.