Přehrát tento tutoriál obsahuje související videokurz vytvořený týmem Real Python. Podívejte se na to společně s písemným výukovým programem, který prohloubí vaše porozumění:Django Migrations 101
Od verze 1.7 přichází Django s vestavěnou podporou migrací databází. V Django jdou migrace databází obvykle ruku v ruce s modely:kdykoli naprogramujete nový model, vygenerujete také migraci k vytvoření potřebné tabulky v databázi. Migrace však umí mnohem více.
V průběhu čtyř článků a jednoho videa se dozvíte, jak migrace Django fungují a jak z nich můžete vytěžit maximum:
- Část 1:Django Migrations:A Primer (aktuální článek)
- Část 2:Ponoření se hlouběji do migrace
- Část 3:Migrace dat
- Video:Migrace Django 1.7 – základní verze
V tomto článku se seznámíte s migrací Django a naučíte se následující:
- Jak vytvořit databázové tabulky bez psaní SQL
- Jak automaticky upravit databázi po změně modelů
- Jak vrátit změny provedené v databázi
Box zdarma: Kliknutím sem získáte přístup k bezplatné příručce Django Learning Resources Guide (PDF), která vám ukáže tipy a triky a také běžné nástrahy, kterým je třeba se vyhnout při vytváření webových aplikací Python + Django.
Problémy, které migrace řeší
Pokud s Django nebo vývojem webu obecně začínáte, možná neznáte koncept migrace databází a nemusí vám připadat jasné, proč je to dobrý nápad.
Nejprve rychle definujme pár pojmů, abychom se ujistili, že jsou všichni na stejné stránce. Django je navrženo pro práci s relační databází uloženou v systému správy relačních databází, jako je PostgreSQL, MySQL nebo SQLite.
V relační databázi jsou data organizována v tabulkách. Databázová tabulka má určitý počet sloupců, ale může mít libovolný počet řádků. Každý sloupec má specifický datový typ, například řetězec určité maximální délky nebo kladné celé číslo. Popis všech tabulek s jejich sloupci a jejich příslušnými datovými typy se nazývá databázové schéma.
Všechny databázové systémy podporované Django používají jazyk SQL k vytváření, čtení, aktualizaci a mazání dat v relační databázi. SQL se také používá k vytváření, změnám a odstraňování samotných databázových tabulek.
Přímá práce s SQL může být značně těžkopádná, takže pro usnadnění vašeho života přichází Django s objektově relačním mapovačem, zkráceně ORM. ORM mapuje relační databázi do světa objektově orientovaného programování. Místo definování databázových tabulek v SQL píšete modely Django v Pythonu. Vaše modely definují databázová pole, která odpovídají sloupcům v jejich databázových tabulkách.
Zde je příklad toho, jak je třída modelu Django mapována na databázovou tabulku:
Ale pouhé definování modelové třídy v souboru Python nezpůsobí, že se databázová tabulka jako kouzlem objeví odnikud. Vytvoření databázových tabulek pro uložení vašich modelů Django je úkolem migrace databáze. Kromě toho, kdykoli provedete změnu ve svých modelech, jako je přidání pole, musí být změněna také databáze. Migrace to řeší také.
Zde je několik způsobů, jak vám migrace Django usnadní život.
Provádění změn databáze bez SQL
Bez migrace byste se museli připojit ke své databázi a zadat spoustu SQL příkazů nebo použít grafický nástroj, jako je PHPMyAdmin, abyste upravili schéma databáze pokaždé, když byste chtěli změnit definici modelu.
V Django jsou migrace primárně napsány v Pythonu, takže nemusíte znát žádné SQL, pokud nemáte opravdu pokročilé případy použití.
Vyhýbání se opakování
Vytvoření modelu a následné psaní SQL pro vytvoření databázových tabulek pro něj by se opakovalo.
Migrace jsou generovány z vašich modelů, takže se nebudete opakovat.
Zajištění definic modelů a schématu databáze v synchronizaci
Obvykle máte více instancí své databáze, například jednu databázi pro každého vývojáře ve vašem týmu, databázi pro testování a databázi s živými daty.
Bez migrací budete muset provést jakékoli změny schématu na každé z vašich databází a budete muset sledovat, které změny již byly v které databázi provedeny.
S Django Migrations můžete snadno udržovat více databází v synchronizaci s vašimi modely.
Sledování změny schématu databáze v řízení verzí
Systém správy verzí, jako je Git, je vynikající pro kód, ale ne tolik pro databázová schémata.
Vzhledem k tomu, že migrace jsou v Djangu prostým Pythonem, můžete je umístit do systému správy verzí stejně jako jakýkoli jiný kód.
Nyní jste snad přesvědčeni, že migrace jsou užitečným a mocným nástrojem. Začněme se učit, jak tuto sílu uvolnit.
Nastavení projektu Django
V tomto tutoriálu budete pracovat na jednoduché aplikaci pro sledování bitcoinů jako na příkladu projektu.
Prvním krokem je instalace Django. Zde je návod, jak to udělat na Linuxu nebo macOS X pomocí virtuálního prostředí:
$ python3 -m venv env
$ source env/bin/activate
(env) $ pip install "Django==2.1.*"
...
Successfully installed Django-2.1.3
Nyní jste vytvořili nové virtuální prostředí a aktivovali jej, stejně jako do tohoto virtuálního prostředí nainstalovali Django.
Všimněte si, že ve Windows byste spustili env/bin/activate.bat
místo source env/bin/activate
pro aktivaci vašeho virtuálního prostředí.
Pro snadnější čitelnost nebudou příklady konzoly obsahovat (env)
od této chvíle součástí výzvy.
S nainstalovaným Django můžete vytvořit projekt pomocí následujících příkazů:
$ django-admin.py startproject bitcoin_tracker
$ cd bitcoin_tracker
$ python manage.py startapp historical_data
Získáte tak jednoduchý projekt a aplikaci s názvem historical_data
. Nyní byste měli mít tuto adresářovou strukturu:
bitcoin_tracker/
|
├── bitcoin_tracker/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
|
├── historical_data/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations/
│ │ └── __init__.py
| |
│ ├── models.py
│ ├── tests.py
│ └── views.py
|
└── manage.py
V rámci bitcoin_tracker
v adresáři, existují dva podadresáře:bitcoin_tracker
pro soubory celého projektu a historical_data
obsahující soubory pro aplikaci, kterou jste vytvořili.
Chcete-li nyní vytvořit model, přidejte tuto třídu do historical_data/models.py
:
class PriceHistory(models.Model):
date = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=7, decimal_places=2)
volume = models.PositiveIntegerField()
Toto je základní model pro sledování cen bitcoinů.
Nezapomeňte také přidat nově vytvořenou aplikaci do settings.INSTALLED_APPS
. Otevřete bitcoin_tracker/settings.py
a připojte historical_data
do seznamu INSTALLED_APPS
, takto:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'historical_data',
]
Ostatní nastavení jsou pro tento projekt v pořádku. Tento výukový program předpokládá, že váš projekt je nakonfigurován pro použití databáze SQLite, což je výchozí nastavení.
Vytváření migrací
Po vytvoření modelu je první věcí, kterou musíte udělat, vytvořit pro něj migraci. Můžete to provést pomocí následujícího příkazu:
$ python manage.py makemigrations historical_data
Migrations for 'historical_data':
historical_data/migrations/0001_initial.py
- Create model PriceHistory
Poznámka: Zadání názvu aplikace, historical_data
, je volitelný. Ponecháte-li ji vypnutou, dojde k migraci všech aplikací.
Tím se vytvoří soubor migrace, který Django instruuje, jak vytvořit databázové tabulky pro modely definované ve vaší aplikaci. Podívejme se ještě jednou na strom adresářů:
bitcoin_tracker/
|
├── bitcoin_tracker/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
|
├── historical_data/
│ ├── migrations/
│ │ ├── 0001_initial.py
│ │ └── __init__.py
| |
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
|
├── db.sqlite3
└── manage.py
Jak můžete vidět, migrations
adresář nyní obsahuje nový soubor:0001_initial.py
.
Poznámka: Můžete si všimnout, že spuštění makemigrations
příkaz také vytvořil soubor db.sqlite3
, který obsahuje vaši databázi SQLite.
Když se pokusíte o přístup k neexistujícímu databázovému souboru SQLite3, bude automaticky vytvořen.
Toto chování je jedinečné pro SQLite3. Pokud používáte jakýkoli jiný databázový backend, jako je PostgreSQL nebo MySQL, musíte si databázi vytvořit sami před spuštění makemigrations
.
Do databáze můžete nahlédnout pomocí dbshell
řídící příkaz. V SQLite je příkaz pro výpis všech tabulek jednoduše .tables
:
$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
sqlite>
Databáze je stále prázdná. To se změní, když použijete migraci. Zadejte .quit
pro ukončení prostředí SQLite.
Použití migrací
Nyní jste vytvořili migraci, ale chcete-li skutečně provést změny v databázi, musíte ji použít příkazem pro správu migrate
:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying historical_data.0001_initial... OK
Applying sessions.0001_initial... OK
Tady se toho děje hodně! Podle výstupu byla vaše migrace úspěšně použita. Ale odkud se berou všechny ostatní migrace?
Pamatujte na nastavení INSTALLED_APPS
? Některé z dalších aplikací, které jsou zde uvedeny, také přicházejí s migrací a migrate
příkaz management standardně použije migraci pro všechny nainstalované aplikace.
Podívejte se ještě jednou na databázi:
$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .tables
auth_group django_admin_log
auth_group_permissions django_content_type
auth_permission django_migrations
auth_user django_session
auth_user_groups historical_data_pricehistory
auth_user_user_permissions
sqlite>
Nyní existuje více tabulek. Jejich názvy vám dávají představu o jejich účelu. Migrace, kterou jste vygenerovali v předchozím kroku, vytvořila historical_data_pricehistory
stůl. Pojďme to zkontrolovat pomocí .schema
příkaz:
sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory"(
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" integer unsigned NOT NULL
);
.schema
příkaz vypíše CREATE
příkaz, který byste provedli, abyste vytvořili tabulku. Parametr --indent
pěkně to naformátuje. I když nejste obeznámeni se syntaxí SQL, můžete vidět, že schéma historical_data_pricehistory
tabulka odráží pole PriceHistory
model.
Pro každé pole je sloupec a další sloupec id
pro primární klíč, který Django vytvoří automaticky, pokud ve svém modelu explicitně neurčíte primární klíč.
Zde je to, co se stane, když spustíte migrate
příkaz znovu:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
No migrations to apply.
Nic! Django si pamatuje, které migrace již byly použity, a nepokouší se je znovu spustit.
Stojí za zmínku, že můžete také omezit migrate
příkaz správy do jedné aplikace:
$ python manage.py migrate historical_data
Operations to perform:
Apply all migrations: historical_data
Running migrations:
No migrations to apply.
Jak můžete vidět, Django nyní používá migraci pouze pro historical_data
aplikace.
Když spouštíte migraci poprvé, je dobré použít všechny migrace, abyste se ujistili, že vaše databáze obsahuje potřebné tabulky pro funkce, které můžete považovat za samozřejmé, jako je ověřování uživatelů a relace.
Změna modelů
Vaše modely nejsou vytesané do kamene. Vaše modely se změní, jak váš projekt Django získá více funkcí. Můžete přidat nebo odebrat pole nebo změnit jejich typy a možnosti.
Když změníte definici modelu, musí se změnit i databázové tabulky používané k ukládání těchto modelů. Pokud vaše definice modelu neodpovídají vašemu aktuálnímu schématu databáze, pravděpodobně narazíte na django.db.utils.OperationalError
.
Jak tedy změníte databázové tabulky? Vytvořením a použitím migrace.
Při testování vašeho bitcoinového sledovače si uvědomíte, že jste udělali chybu. Lidé prodávají zlomky bitcoinu, takže pole volume
by měl být typu DecimalField
místo PositiveIntegerField
.
Změňme model tak, aby vypadal takto:
class PriceHistory(models.Model):
date = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=7, decimal_places=2)
volume = models.DecimalField(max_digits=7, decimal_places=3)
Bez migrací byste museli zjistit syntaxi SQL, abyste změnili PositiveIntegerField
do DecimalField
. Naštěstí to za vás vyřídí Django. Stačí mu říct, aby provedl migraci:
$ python manage.py makemigrations
Migrations for 'historical_data':
historical_data/migrations/0002_auto_20181112_1950.py
- Alter field volume on pricehistory
Poznámka: Název migračního souboru (0002_auto_20181112_1950.py
) je založen na aktuálním čase a bude se lišit, pokud jej budete sledovat ve svém systému.
Nyní použijete tuto migraci na svou databázi:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
Applying historical_data.0002_auto_20181112_1950... OK
Migrace byla úspěšně použita, takže můžete použít dbshell
abyste ověřili, že změny měly účinek:
$ python manage.py dbshell
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite> .schema --indent historical_data_pricehistory
CREATE TABLE IF NOT EXISTS "historical_data_pricehistory" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" decimal NOT NULL
);
Pokud porovnáte nové schéma se schématem, které jste viděli dříve, všimnete si, že typ volume
sloupec se změnil z integer
na decimal
odrážet změnu volume
pole v modelu z PositiveIntegerField
na DecimalField
.
Výpis migrací
Pokud chcete vědět, jaké migrace existují v projektu Django, nemusíte se prohrabovat v migrations
adresáře vašich nainstalovaných aplikací. Můžete použít showmigrations
příkaz:
$ ./manage.py showmigrations
admin
[X] 0001_initial
[X] 0002_logentry_remove_auto_add
[X] 0003_logentry_add_action_flag_choices
auth
[X] 0001_initial
[X] 0002_alter_permission_name_max_length
[X] 0003_alter_user_email_max_length
[X] 0004_alter_user_username_opts
[X] 0005_alter_user_last_login_null
[X] 0006_require_contenttypes_0002
[X] 0007_alter_validators_add_error_messages
[X] 0008_alter_user_username_max_length
[X] 0009_alter_user_last_name_max_length
contenttypes
[X] 0001_initial
[X] 0002_remove_content_type_name
historical_data
[X] 0001_initial
[X] 0002_auto_20181112_1950
sessions
[X] 0001_initial
Zde jsou uvedeny všechny aplikace v projektu a migrace spojené s každou aplikací. Také to vloží velké X
vedle migrací, které již byly použity.
Pro náš malý příklad showmigrations
příkaz není nijak zvlášť vzrušující, ale přijde vhod, když začínáte pracovat na existující kódové základně nebo pracujete v týmu, kde nejste jedinou osobou, která přidává migraci.
Odstranění migrací
Nyní víte, jak provádět změny ve schématu databáze vytvořením a použitím migrací. V určitém okamžiku možná budete chtít vrátit zpět změny a přepnout zpět na dřívější schéma databáze, protože:
- Chcete otestovat migraci, kterou napsal kolega
- Uvědomte si, že změna, kterou jste provedli, byl špatný nápad
- Paralelně pracovat na více funkcích s různými změnami databáze
- Chcete obnovit zálohu, která byla vytvořena, když databáze měla ještě starší schéma
Naštěstí migrace nemusí být jednosměrná. V mnoha případech lze účinky migrace zrušit zrušením aplikace migrace. Chcete-li migraci zrušit, musíte zavolat migrate
s názvem aplikace a názvem migrace před migraci, kterou chcete zrušit.
Pokud chcete migraci vrátit zpět 0002_auto_20181112_1950
v historical_data
aplikaci, musíte předat 0001_initial
jako argument pro migrate
příkaz:
$ python manage.py migrate historical_data 0001_initial
Operations to perform:
Target specific migration: 0001_initial, from historical_data
Running migrations:
Rendering model states... DONE
Unapplying historical_data.0002_auto_20181112_1950... OK
Migrace nebyla použita, což znamená, že změny v databázi byly vráceny.
Zrušením použití migrace se neodstraní její migrační soubor. Při příštím spuštění migrate
migrace bude znovu použita.
Upozornění: Nepleťte si zrušení migrace s operací zpět, na kterou jste zvyklí z vašeho oblíbeného textového editoru.
Ne všechny databázové operace lze zcela vrátit zpět. Pokud z modelu odstraníte pole, vytvoříte migraci a použijete ji, Django odstraní příslušný sloupec z databáze.
Zrušením použití této migrace se sloupec znovu vytvoří, ale nevrátí se zpět data, která byla v tomto sloupci uložena!
Když řešíte názvy migrace, Django vám ušetří několik úhozů, protože vás nenutí vyslovovat celý název migrace. Potřebuje jen tolik názvu, aby jej bylo možné jednoznačně identifikovat.
V předchozím příkladu by stačilo spustit python manage.py migrate historical_data 0001
.
Pojmenování migrací
Ve výše uvedeném příkladu Django přišel s názvem migrace na základě časového razítka – něco jako *0002_auto_20181112_1950
. Pokud s tím nejste spokojeni, můžete použít --name
parametr k poskytnutí vlastního názvu (bez .py
rozšíření).
Chcete-li to vyzkoušet, musíte nejprve odstranit starou migraci. Již jste jej zrušili, takže můžete soubor bezpečně smazat:
$ rm historical_data/migrations/0002_auto_20181112_1950.py
Nyní jej můžete znovu vytvořit s popisnějším názvem:
$ ./manage.py makemigrations historical_data --name switch_to_decimals
Tím se vytvoří stejná migrace jako předtím, s výjimkou nového názvu 0002_switch_to_decimals
.
Závěr
V tomto tutoriálu jste probrali docela kus země a naučili jste se základy migrace Django.
Abychom to shrnuli, základní kroky pro použití migrací Django vypadají takto:
- Vytvořte nebo aktualizujte model
- Spusťte
./manage.py makemigrations <app_name>
- Spusťte
./manage.py migrate
pro migraci všeho nebo./manage.py migrate <app_name>
k migraci jednotlivé aplikace - Opakujte podle potřeby
A je to! Tento pracovní postup bude fungovat většinu času, ale pokud věci nefungují podle očekávání, také víte, jak migrace vypsat a zrušit.
Pokud jste dříve vytvářeli a upravovali své databázové tabulky pomocí ručně psaného SQL, nyní jste se stali mnohem efektivnějšími, když jste tuto práci delegovali na migrace Django.
V dalším tutoriálu v této sérii se ponoříte hlouběji do tématu a dozvíte se, jak funguje migrace Django pod pokličkou.
Box zdarma: Kliknutím sem získáte přístup k bezplatné příručce Django Learning Resources Guide (PDF), která vám ukáže tipy a triky a také běžné nástrahy, kterým je třeba se vyhnout při vytváření webových aplikací Python + Django.
Na zdraví!