Toto je druhý článek v naší sérii migrací Django:
- Část 1:Django Migrations:A Primer
- Část 2:Pronikání hlouběji do migrace Django (aktuální článek)
- Část 3:Migrace dat
- Video:Django 1.7 Migrations – A Primer
V předchozím článku této série jste se dozvěděli o účelu migrací Django. Seznámili jste se se základními způsoby používání, jako je vytváření a aplikace migrací. Nyní je čas ponořit se hlouběji do migračního systému a podívat se na některé z jeho základních mechanismů.
Na konci tohoto článku budete vědět:
- Jak Django sleduje migraci
- Jak migrace poznají, které databázové operace mají provést
- Jak jsou definovány závislosti mezi migrací
Jakmile si zabalíte hlavu do této části migračního systému Django, budete dobře připraveni vytvořit si vlastní migrace. Pojďme rovnou tam, kde jsme skončili!
Tento článek používá bitcoin_tracker
Projekt Django postavený v Django Migrations:A Primer. Tento projekt můžete buď znovu vytvořit procházením tohoto článku, nebo si můžete stáhnout zdrojový kód:
Stáhnout zdrojový kód: Klikněte sem a stáhněte si kód pro projekt migrace Django, který budete používat v tomto článku.
Jak Django ví, které migrace použít
Pojďme si zrekapitulovat úplně poslední krok předchozího článku ze série. Vytvořili jste migraci a poté jste použili všechny dostupné migrace pomocí python manage.py migrate
.Pokud tento příkaz proběhl úspěšně, pak vaše databázové tabulky nyní odpovídají definicím vašeho modelu.
Co se stane, když tento příkaz spustíte znovu? Vyzkoušíme to:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
No migrations to apply.
Se nic nestalo! Jakmile byla migrace aplikována na databázi, Django tuto migraci na tuto konkrétní databázi znovu nepoužije. Zajištění, že migrace bude použita pouze jednou, vyžaduje sledování migrací, které byly použity.
Django používá databázovou tabulku nazvanou django_migrations
. Django automaticky vytvoří tuto tabulku ve vaší databázi při prvním použití jakékoli migrace. Pro každou migraci, která je použita nebo falešná, je do tabulky vložen nový řádek.
Zde je například ukázka toho, jak tato tabulka vypadá v našem bitcoin_tracker
projekt:
ID | Aplikace | Jméno | Použito |
---|---|---|---|
1 | contenttypes | 0001_initial | 2019-02-05 20:23:21.461496 |
2 | auth | 0001_initial | 2019-02-05 20:23:21.489948 |
3 | admin | 0001_initial | 2019-02-05 20:23:21.508742 |
4 | admin | 0002_logentry_remove... | 2019-02-05 20:23:21.531390 |
5 | admin | 0003_logentry_add_ac... | 2019-02-05 20:23:21.564834 |
6 | contenttypes | 0002_remove_content_... | 2019-02-05 20:23:21.597186 |
7 | auth | 0002_alter_permissio... | 2019-02-05 20:23:21.608705 |
8 | auth | 0003_alter_user_emai... | 2019-02-05 20:23:21.628441 |
9 | auth | 0004_alter_user_user... | 2019-02-05 20:23:21.646824 |
10 | auth | 0005_alter_user_last... | 2019-02-05 20:23:21.661182 |
11 | auth | 0006_require_content... | 2019-02-05 20:23:21.663664 |
12 | auth | 0007_alter_validator... | 2019-02-05 20:23:21.679482 |
13 | auth | 0008_alter_user_user... | 2019-02-05 20:23:21.699201 |
14 | auth | 0009_alter_user_last... | 2019-02-05 20:23:21.718652 |
15 | historical_data | 0001_initial | 2019-02-05 20:23:21.726000 |
16 | sessions | 0001_initial | 2019-02-05 20:23:21.734611 |
19 | historical_data | 0002_switch_to_decimals | 2019-02-05 20:30:11.337894 |
Jak vidíte, pro každou použitou migraci existuje záznam. Tabulka obsahuje nejen migrace z našich historical_data
aplikace, ale také migrace ze všech ostatních nainstalovaných aplikací.
Při příštím spuštění migrace Django přeskočí migrace uvedené v tabulce databáze. To znamená, že i když ručně změníte soubor migrace, která již byla použita, Django bude tyto změny ignorovat, pokud pro ni již existuje záznam v databázi.
Djanga byste mohli přimět k opětovnému spuštění migrace odstraněním odpovídajícího řádku z tabulky, ale to je zřídkakdy dobrý nápad a může vám způsobit nefunkční systém migrace.
Migrační soubor
Co se stane, když spustíte python manage.py makemigrations <appname>
? Django hledá změny provedené na modelech ve vaší aplikaci <appname>
. Pokud nějaké najde, například model, který byl přidán, vytvoří migrační soubor v migrations
podadresář. Tento migrační soubor obsahuje seznam operací pro synchronizaci databázového schématu s definicí modelu.
Poznámka: Vaše aplikace musí být uvedena v INSTALLED_APPS
a musí obsahovat migrations
adresář s __init__.py
soubor. Jinak pro něj Django nevytvoří žádné migrace.
migrations
adresář se automaticky vytvoří, když vytvoříte novou aplikaci pomocí startapp
příkaz pro správu, ale při ručním vytváření aplikace na něj snadno zapomenete.
Migrační soubory jsou pouze Python, takže se podívejme na první migrační soubor v historical_prices
aplikace. Najdete jej na adrese historical_prices/migrations/0001_initial.py
. Mělo by to vypadat nějak takto:
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.CreateModel(
name='PriceHistory',
fields=[
('id', models.AutoField(
verbose_name='ID',
serialize=False,
primary_key=True,
auto_created=True)),
('date', models.DateTimeField(auto_now_add=True)),
('price', models.DecimalField(decimal_places=2, max_digits=5)),
('volume', models.PositiveIntegerField()),
('total_btc', models.PositiveIntegerField()),
],
options={
},
bases=(models.Model,),
),
]
Jak můžete vidět, obsahuje jedinou třídu s názvem Migration
který zdědí z django.db.migrations.Migration
. Toto je třída, kterou migrační rámec vyhledá a spustí, když jej požádáte o použití migrací.
Migration
třída obsahuje dva hlavní seznamy:
dependencies
operations
Migrační operace
Podívejme se na operations
seznam jako první. Tato tabulka obsahuje operace, které mají být provedeny v rámci migrace. Operace jsou podtřídy třídy django.db.migrations.operations.base.Operation
. Zde jsou běžné operace, které jsou zabudovány do Django:
Operační třída | Popis |
---|---|
CreateModel | Vytvoří nový model a odpovídající databázovou tabulku |
DeleteModel | Odstraní model a zruší jeho databázovou tabulku |
RenameModel | Přejmenuje model a přejmenuje jeho databázovou tabulku |
AlterModelTable | Přejmenuje databázovou tabulku pro model |
AlterUniqueTogether | Změní jedinečná omezení modelu |
AlterIndexTogether | Změní indexy modelu |
AlterOrderWithRespectTo | Vytvoří nebo odstraní _order sloupec pro model |
AlterModelOptions | Změní různé možnosti modelu bez ovlivnění databáze |
AlterModelManagers | Změní dostupné správce během migrace |
AddField | Přidá pole do modelu a odpovídající sloupec do databáze |
RemoveField | Odebere pole z modelu a odstraní odpovídající sloupec z databáze |
AlterField | Změní definici pole a v případě potřeby změní jeho databázový sloupec |
RenameField | Přejmenuje pole a v případě potřeby také jeho databázový sloupec |
AddIndex | Vytvoří index v databázové tabulce pro model |
RemoveIndex | Odebere index z databázové tabulky pro model |
Všimněte si, jak jsou operace pojmenovány podle změn provedených v definicích modelu, nikoli podle akcí, které se provádějí v databázi. Když použijete migraci, každá operace je zodpovědná za generování nezbytných příkazů SQL pro vaši konkrétní databázi. Například CreateModel
vygeneruje CREATE TABLE
SQL příkaz.
Po vybalení mají migrace podporu pro všechny standardní databáze, které Django podporuje. Pokud se tedy budete držet zde uvedených operací, můžete ve svých modelech provádět víceméně jakékoli změny, které chcete, aniž byste se museli starat o základní SQL. To je vše za vás.
Poznámka: V některých případech nemusí Django správně detekovat vaše změny. Pokud přejmenujete model a změníte několik jeho polí, Django to může zaměnit za nový model.
Místo RenameModel
a několik AlterField
operace, vytvoří DeleteModel
a CreateModel
úkon. Namísto přejmenování databázové tabulky pro model ji zruší a vytvoří novou tabulku s novým názvem, čímž efektivně smaže všechna vaše data!
Zvykněte si zkontrolovat vygenerované migrace a otestovat je na kopii vaší databáze, než je spustíte na produkčních datech.
Django poskytuje tři další třídy operací pro pokročilé případy použití:
RunSQL
umožňuje spouštět vlastní SQL v databázi.RunPython
umožňuje spouštět jakýkoli kód Pythonu.SeparateDatabaseAndState
je specializovaná operace pro pokročilé použití.
S těmito operacemi můžete v podstatě provádět libovolné změny ve vaší databázi. Tyto operace však nenajdete v migraci, která byla vytvořena automaticky pomocí makemigrations
příkaz pro správu.
Od verze Django 2.0 je v django.contrib.postgres.operations
k dispozici také několik operací specifických pro PostgreSQL které můžete použít k instalaci různých rozšíření PostgreSQL:
BtreeGinExtension
BtreeGistExtension
CITextExtension
CryptoExtension
HStoreExtension
TrigramExtension
UnaccentExtension
Všimněte si, že migrace obsahující jednu z těchto operací vyžaduje uživatele databáze s oprávněními superuživatele.
V neposlední řadě si také můžete vytvořit vlastní provozní třídy. Pokud se na to chcete podívat, podívejte se na dokumentaci Django o vytváření vlastních operací migrace.
Závislosti migrace
dependencies
seznam v migrační třídě obsahuje všechny migrace, které musí být použity před použitím této migrace.
V souboru 0001_initial.py
migraci, kterou jste viděli výše, nemusí být nic použito předtím, takže neexistují žádné závislosti. Podívejme se na druhou migraci v historical_prices
aplikace. V souboru 0002_switch_to_decimals.py
, dependencies
atribut Migration
má záznam:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('historical_data', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='pricehistory',
name='volume',
field=models.DecimalField(decimal_places=3, max_digits=7),
),
]
Výše uvedená závislost říká, že migrace 0001_initial
historical_data
aplikace musí být spuštěn jako první. To dává smysl, protože migrace 0001_initial
vytvoří tabulku obsahující pole, které migrace 0002_switch_to_decimals
chce změnit.
Migrace může také záviset na migraci z jiné aplikace, jako je tato:
class Migration(migrations.Migration):
...
dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
]
To je obvykle nutné, pokud má model cizí klíč ukazující na model v jiné aplikaci.
Případně můžete také vynutit spuštění migrace před další migrace pomocí atributu run_before
:
class Migration(migrations.Migration):
...
run_before = [
('third_party_app', '0001_initial'),
]
Závislosti lze také kombinovat, takže můžete mít více závislostí. Tato funkce poskytuje velkou flexibilitu, protože můžete použít cizí klíče, které závisí na modelech z různých aplikací.
Možnost explicitně definovat závislosti mezi migracemi také znamená, že číslování migrací (obvykle 0001
, 0002
, 0003
, …) nereprezentuje striktně pořadí, ve kterém jsou migrace aplikovány. Můžete přidat libovolnou závislost, a tak řídit pořadí, aniž byste museli přečíslovat všechny migrace.
Zobrazení migrace
Obecně se nemusíte starat o SQL, které migrace generuje. Ale pokud si chcete znovu ověřit, že vygenerovaný SQL dává smysl, nebo jste jen zvědaví, jak to vypadá, pak vám Django's pomůže s sqlmigrate
příkaz pro správu:
$ python manage.py sqlmigrate historical_data 0001
BEGIN;
--
-- Create model PriceHistory
--
CREATE TABLE "historical_data_pricehistory" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" integer unsigned NOT NULL
);
COMMIT;
Tím se zobrazí seznam základních SQL dotazů, které budou generovány zadanou migrací, na základě databáze ve vašem settings.py
soubor. Když předáte parametr --backwards
, Django vygeneruje SQL pro zrušení migrace:
$ python manage.py sqlmigrate --backwards historical_data 0001
BEGIN;
--
-- Create model PriceHistory
--
DROP TABLE "historical_data_pricehistory";
COMMIT;
Jakmile uvidíte výstup sqlmigrate
pro trochu složitější migraci možná oceníte, že nemusíte všechny tyto SQL vytvářet ručně!
Jak Django zjišťuje změny vašich modelů
Viděli jste, jak migrační soubor vypadá a jak jeho seznam Operation
tříd definuje změny provedené v databázi. Ale jak přesně Django ví, které operace by měly jít do migračního souboru? Můžete očekávat, že Django porovná vaše modely s vaším databázovým schématem, ale není tomu tak.
Při spuštění makemigrations
, Django ne zkontrolovat vaši databázi. Neporovnává ani soubor vašeho modelu s předchozí verzí. Místo toho Django prochází všemi migracemi, které byly použity, a vytváří stav projektu, jak by modely měly vypadat. Tento stav projektu je poté porovnán s vašimi aktuálními definicemi modelu a je vytvořen seznam operací, které při použití aktualizují stav projektu s definicemi modelu.
Hraní šachů s Django
Své modely si můžete představit jako šachovnici a Django je šachový velmistr, který vás sleduje, jak hrajete sami proti sobě. Ale velmistr nesleduje každý váš pohyb. Velmistr se dívá na tabuli pouze tehdy, když křičíte makemigrations
.
Protože existuje pouze omezená sada možných tahů (a velmistr je velmistr), může přijít s tahy, které se staly od doby, kdy se naposledy podívala na šachovnici. Udělá si poznámky a nechá vás hrát, dokud nezakřičíte makemigrations
znovu.
Při příštím pohledu na šachovnici si velmistr nepamatuje, jak šachovnice vypadala naposledy, ale může si projít své poznámky z předchozích tahů a vytvořit si mentální model toho, jak šachovnice vypadala.
Teď, když zakřičíte migrate
, velmistr přehraje všechny zaznamenané tahy na jiné šachovnici a zaznamená do tabulky, které z jejích záznamů již byly použity. Tato druhá šachovnice je vaše databáze a tabulka je django_migrations
tabulka.
Tato analogie je docela vhodná, protože pěkně ilustruje některé chování migrací Django:
-
Migrace Django se snaží být efektivní: Stejně jako velmistr předpokládá, že jste provedli nejmenší počet tahů, Django se pokusí vytvořit nejúčinnější migrace. Pokud přidáte pole s názvem
A
na model a poté jej přejmenujte naB
a poté spusťtemakemigrations
, pak Django vytvoří novou migraci k přidání pole s názvemB
. -
Migrace Django má své limity: Pokud uděláte hodně tahů, než necháte velmistra podívat se na šachovnici, pak nemusí být schopna vysledovat přesné pohyby každé figurky. Podobně nemusí Django přijít se správnou migrací, pokud provedete příliš mnoho změn najednou.
-
Migrace Django od vás očekává, že budete hrát podle pravidel: Když uděláte něco neočekávaného, například sundáte náhodnou figurku ze šachovnice nebo si pohrajete s notami, velmistr si toho zpočátku nemusí všimnout, ale dříve nebo později rozhodí rukama a odmítne pokračovat. Totéž se stane, když si pohrajete s
django_migrations
tabulky nebo změnit schéma databáze mimo migraci, například odstraněním databázové tabulky pro model.
Porozumění SeparateDatabaseAndState
Nyní, když víte o stavu projektu, který Django staví, je čas podívat se blíže na operaci SeparateDatabaseAndState
. Tato operace dokáže přesně to, co název napovídá:může oddělit stav projektu (mentální model, který Django sestavuje) od vaší databáze.
SeparateDatabaseAndState
je vytvořena instancí se dvěma seznamy operací:
state_operations
obsahuje operace, které se aplikují pouze na stav projektu.database_operations
obsahuje operace, které jsou aplikovány pouze na databázi.
Tato operace vám umožňuje provést jakoukoli změnu ve vaší databázi, ale je vaší odpovědností zajistit, aby stav projektu následně odpovídal databázi. Příklady případů použití pro SeparateDatabaseAndState
přesouváte model z jedné aplikace do druhé nebo vytváříte index na velké databázi bez prostojů.
SeparateDatabaseAndState
je pokročilá operace a nebudete muset pracovat s migrací první den a možná ani nikdy. SeparateDatabaseAndState
je podobná operaci srdce. Nese to docela dost rizika a neděláte to jen pro zábavu, ale někdy je to nezbytný postup, jak udržet pacienta naživu.
Závěr
Tím končí váš hluboký ponor do migrace Django. Gratulujeme! Probrali jste spoustu pokročilých témat a nyní dobře rozumíte tomu, co se děje pod pokličkou migrace.
Dozvěděli jste se, že:
- Django sleduje aplikované migrace v tabulce migrací Django.
- Migrace Django se skládají z prostých souborů Pythonu obsahujících
Migration
třída. - Django ví, které změny má provést z
operations
v seznamuMigration
třídy. - Django porovná vaše modely se stavem projektu, který vytvoří z migrací.
S těmito znalostmi jste nyní připraveni pustit se do třetí části série o migracích Django, kde se dozvíte, jak používat migraci dat k bezpečnému provádění jednorázových změn ve vašich datech. Zůstaňte naladěni!
V tomto článku byl použit bitcoin_tracker
Projekt Django postavený v Django Migrations:A Primer. Tento projekt můžete buď znovu vytvořit pomocí tohoto článku, nebo si můžete stáhnout zdrojový kód:
Stáhnout zdrojový kód: Klikněte sem a stáhněte si kód pro projekt migrace Django, který budete používat v tomto článku.