sql >> Databáze >  >> RDS >> Database

Pronikání hlouběji do migrace Django

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:

  1. dependencies
  2. 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í:

  1. RunSQL umožňuje spouštět vlastní SQL v databázi.
  2. RunPython umožňuje spouštět jakýkoli kód Pythonu.
  3. 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 na B a poté spusťte makemigrations , pak Django vytvoří novou migraci k přidání pole s názvem B .

  • 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í:

  1. state_operations obsahuje operace, které se aplikují pouze na stav projektu.
  2. 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 seznamu Migration 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.



  1. Úlohy hybridní databáze OLTP/Analytics v clusteru Galera využívající asynchronní podřízené jednotky

  2. SQLSTATE[HY093]:Neplatné číslo parametru:parametr nebyl definován

  3. Alternativní výstupní formát pro psql

  4. Vytváření částečného jedinečného indexu pomocí sqlalchemy na Postgres