Django

Acc-South

Django od verze 1.7 bude mít vlastní nástroje na migrace. South už bude zbytečný.

Django South

Úvodní prohlášení: South užívám občas, takže vždy stačím zapomenout, jak se to vlastně dělalo. Toto jsou poznámky pro moje rychlejší rozpomenutí. Snažil jsem se to ale napsat tak, aby tomu rozumněl i někdo jiný. Snad se mi to povedlo :)

South je rozšíření frameworku Django o možnosti dělat změny v popisu modelů dat (i samotných dat) a promítnout je do databázového schématu. Django samotné umí pouze databázi z modelů vytvořit (syncdb), neumí je ale měnit ani mazat.

Migrace se ukládají vždy ke konkrétní aplikaci (není jedna pro celý project). Není-li v aplikaci migrace definovaná (nemá podadresář migrations), South jí jednoduše ignoruje.

South umožňuje i zpětné změny - odvolání migrací (pokročilé).

Dobrá rada: Změny v modelech provádějte atomickým způsobem - jedna konktrétní migrace by měla odpovídat jediné konkrétní změně v modelu. Při "hromadných" změnách si to roz...(nebo jak to říci slušně). Samozřejmě může být vhodné připravit si několik dílčích schemamigration a pak to vše spustit naráz migrate.

1.  Instalace

  • nainstalovat python-django-south (Debian), nebo přes pip a pod (nejnovější verze).
  • settings.py - INSTALLED_APPS, přidat 'south',
  • spustit manage.py syncdb - vytvoří se potřebné pomocné tabulky v databázi

V manage.py přibudou možnosti pro South.

2.  Základní schéma migrace

Jednotilvé migrace budou zapisovány v podadresáři migrations příslušné aplikace, a to do souborů, jejichž název začíná pořadovým číslem migrace (00013_...) a pokračuje názvem, který zpravidla popisuje, co se v migraci dělo..

Migrace probíhá ve dvou fázích:

 $ ./manage.py schemamigration app_name --auto
 $ ./manage.py migrate app_name

V první fázi se připraví zmíněný soubor pro migraci, ve druhé se migrace provede.

Následují popisy pro výjimečné situace, kterých je ale většina ;-)

3.  Úvodní migrace

Před rutinním použitím South, je potřeba připravit prostředí. Úvodní migrace vytvoří podadresář migrations v adresáři aplikace app_name a udělá v něm úvodní nastavení. Jsou dvě varianty:

Pro zcela novou aplikaci:

Zcela nová aplikace nemá ještě žádné tabulky v databázi - jsou definovány modely, ale nedělali jsme nad nimi dosud manage.py syncdb.

 $ manage.py schemamigration app_name --initial

Pro již existující aplikaci:

To bude obvyklejší varianta - máme už databázi i s nějakými cvičnými daty a nyní narazíme na problém, jak měnit modely a následně schéma databáze. Tak nainstalujeme South a... tohle je náš první příkaz:

 $ manage.py convert_to_south app_name

Pozor: tento příkaz spouštět ve stavu, kdy modely odpovídají tomu, co je v databázi. Teprve po tomto příkazu se můžeme pustit do vytoužených změn. Je-li model jiný, než tabulka v databázi, South to pozná, zahlásí chybu a schválně zhavaruje.

3.1  Spuštění vlastní migrace

Po úvodním příkaze, zaznamenáme tuto úvodní migraci do databáze:

 $ ./manage.py migrate app_name

4.  Migrace po změně modelu (schématu)

Všechy migrace v této kapitole se týkají změn v popisu modelu, který nemá vliv na stav dat. Datové migrace jsou popsány v další kapitole.

 $ manage.py schemamigration app_name --auto

4.1  Přidání / Odbrání sloupce

Přidání, nebo odebrání sloupce v modelu vyžaduje buď nastavené default=, nebo null=True. Ano, i při odebrání sloupce - South umí i vracet změny, takže vrácení smazaného sloupce je vlastně vytvoření sloupce:). Není-li default hodnota, pak bude South požadovat její doplnění do modelu, nebo nám umožní zadat ji jednorázově:

 ?  1. Quit now, and add a default to the field in models.py
 ?  2. Specify a one-off value to use for existing columns now
 ? Please select a choice:

Dobře připravenou migraci pak spustíme obvyklým způsobem:

 $ ./manage.py migrate app_name

4.2  Přejmenování sloupce

Tohle v jádru nejde, ale neboj.

  • Přejmenuj si sloupec v definici modelu (př. ulice -> street v modelu Person) a udělej normální schemamigration --auto.
  • Vzniklý soubbor migrace si otevři a u forward a backward najdeš něco o odstranění a přidání sloupců, to smaž a místo toho tam dej:
    def forwards(self, orm):
        # Renaming field 'Person.ulice'
        db.rename_column(u'myapp_person', 'ulice', 'street')

    def backwards(self, orm):
        db.rename_column(u'myapp_person', 'street', 'ulice')
  • Pod těmito řádky je takový nějaký maglajs vytvořený Southem, který je v pořádku, ale můžeš si tam zkontrolovat v tvém modelu, že tam je nové jméno sloupce.
  • Ulož to a pro pořádek soubor s migrací přejmenuj, aby název nemluvil o mazání a přídávání sloupce, ale o přejmenovávání (nepovinné, ale skvělé).
  • A teď už normálně migrate appname.

Poznámka: Někde na netu jsem našel návod, který obsahoval jen ty for- a back- ward, ale ne to pod tím, co tam South dá sám (vynachala se tedy fáze schemamigration --auto). Migrace proběhla, ale pokus do další migraci se schemamigration --auto South odmítal udělat s tím, že aplikace není "freeze" - to je předpokládám ten maglajs pod popisem migrace v souboru migrace. Takže --auto, --auto, --auto.

Zajímavé může být využít parametr --empty příkazu schemamigration. To vytvoří nový "prázdný" soubor pro migraci. Prázdný v tom smyslu, že obsahuje všechno, ale forwards a backwards obsahují pouze příkaz "pass". Tento soubor pak upravíme výše uvedeným způosobem:

 python manage.py schemamigration appname mgrationname --empty

4.3  Oprava poslední migrace (případ "překlep";)

Stane se, že po provedení migrace objevíme chybu (překlep v názvu pole, typ pole...). Pak není nutné připravovat novou migraci, ale tu poslední můžeme update:

 $ manage.py schemamigration app_name --auto --update

South nejdříve rollback odvolá tu posledně provedenou migraci, opraví její soubor a tuto migraci (již opravenou) udělá znovu. (Zakončíme samozřjemě manage.py migrate app_name).

4.4  Problém s Unique

Přidáme-li k existujícímu sloupci parametr unique=True, pak při migraci může být objevena ne-unikátnost a migrace zhavaruje. Databázi "zunifikovat" předem. Týká se i Meta... unique_together.

4.5  ManyToMany - zprostředkující tabulka

Přidáme-li omezení ManyToMany, South je objeví a automaticky vytvoří potřebnou tabulku pro zprostředkování vztahu. Při odebrání omezení z modelu, tuto tabulku zase smaže.

Výjimkou je omezení s parametrem through=tablename - tato tabulka funguje jako samostatný model, takže je řešena nezávilse na tomto omezení (není s ním vytvářena/mazána).

4.6  Námi vytvořená nestandardní pole

South je neumí obsloužit a vyžaduje to trochu práce navíc. Viz orig. dokumentace.

5.  Vypsání provedených migrací

 $ ./manage.py migrate [app_name] --list

6.  Odvolání migrace

 $ ./manage.py migrate app_name number

Jednoduše nakonec napíšeme číslo migrace z počátku názvu (nebo můžeme zadat název celý). Příkaz migrate vlastně automaticky udělá migraci, která má nejvyšší číslo. Zadáme-li nižší, udělá se vytoužený rollback.

7.  Migrace dat

Není možné změnit typ sloupce a doufat, že se vše stane samo. (čínský mudrc)

Poznámka: Pokud sloupec neobsahuje žádná data (třeba na počátku vývoje), můžete ho klidně přejmenovat.

Zazálohujte si všechna data. (marná rada)

Obvyklý postup bude:

  1. vytvořit nový sloupec se žádoucí definicí (migrace schématu)
  2. provést konverzi dat ze starého sloupce do nového (migrace dat)
  3. odstranit starý sloupec (migrace schématu)

Body 1 a 3 umíme. Přesto vše souhrnně:

7.1  1. vytvoříme nový sloupec v modelu a

 $ ./manage.py schemamigration app_name --auto

7.2  2. přpravíme datovou migraci

 $ ./manage.py datamigration app_name migration_name

South vytvoří soubor pro migraci nnnnn_migration_name.py. Ten si upravíme. Jsou tu dvě funkce "forwards" pro žádané změny a "backwards" pro případné vrácení změn. Například:

    def forwards(self, orm):
        for wife in orm.Wife.objects.all():
            wife.ears_size = wife.ears
            wife.save()

    def backwards(self, orm):
        for wife in orm.Wife.objects.all():
            wife.ears = 2
            wife.save()

Zkrátka pro všechny řádky v tabulce uděláme dopřednou konverzi a pak ještě zpětnou. V tomto případě řešíme převod počtu uší na velikost uší u manželek. Dopředu nastavíme velikost ucha podle jejich počtu. Při zpětné konverzi nastavíme natvrdo dvě uši.

Jako název pro migration_name dáváme obvykle jméno přidávaného sloupce.

7.3  3. smázneme nepotřebný sloupec z modelu

7.4  a všechno to necháme proběhnout

 $ ./manage.py migrate app_name

7.5  závěrem

Do třídy Migration souboru pro datovou migraci můžeme přidat jakýkoliv kód - metody. South je bude ignorovat, my je můžeme použít při migraci.

8.  Další dokumentace

South toho umí více. Zde jsem zapsal jen nejpoužívanější základy. Originální dokumentace je na adrese:

http://south.readthedocs.org/en/latest/