Cody Blog

Software development

使用 south 來做 Django database migration

一般在 Django 底下更動 model 的時侯,必須使用 syncdb 這個指令,把修改的 model 同步到 database 之中,但是有個限制,就是只有新的 model 才會被更新,已經存在的 model 就不會更動,此時就需要south的幫忙。在 django 1.7 之後,這個 library 正式被整進到 django 之中。不過目前我手頭上的專案還是以 1.6 為主,所以還是要筆記一下。

初始化 south

安裝完 south 之後,第一次先執行 syncdb 把 south 相關的south_migrationhistory 新增到資料庫中。

:::console
$ ./manage.py syncdb

開始 migration

首先要介紹的是 schemamigration <APP_NAME> --initial,這個指令會在 app 資料夾下面產生一個 migrations 資料夾。如果是第一次執行的話,裡面的檔名會是 0001_initial.py 開頭,裡面會記錄如何從無到有,把資料表建立起來。

:::python
def forwards(self, orm):
    # Adding model 'UserModel'
    db.create_table(u'dusers_dmodel', (
        (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),

此時可以執行 syncdb 做一下確認

:::console
$ python manage.py syncdb
....
Synced:
 > django.contrib.admin
....
Not synced (use migrations):
 - southtut
(use ./manage.py migrate to migrate these)

就會發現 app(southtut) 會放到 Not synced。也就是說從此 syncdb 這個指令就不會再去管 southtut 這個 app 了。此時可能會有兩個情況,如果之前已經先執行過 syncdb,而 database 跟 model 已經完全一致的情況下,我們就必須額外跟 south 說,我們已經同步好了,你不需要幫我們做 migration。這時需要用--fake這個指令。來告訴 south,目前我們的 db 是在 migrations 資料夾的那一個版本。

:::console
$ python manage.py migrate rango 0001 --fake

另一個情況是我們沒有對這個 app 同步過,這時就不能用 syncdb 來做,要改用

:::console
$ python manage.py migrate rango

這時在 south_migrationhistory 這個資料表就會記錄目前這個 app 已經同步到那一個版本

id | app_name | migration | applied ---+------------+------------------+------------------------------- 1 | dusers | 0001_initial | 2014-08-15 10:24:45.694512+08

所以第一次使用的時侯是用 --initial,接下來只要有改變,就可以用--auto。在 model 有修改之後,執行以下指令來建立跟上一個版本的差異資訊到 rango/migrations 資料夾中。

:::console
$ ./manage.py schemamigration rango --auto

過程當中會詢問你如果有新的欄位,是否要填入一預設值。

最後再使用migrate <APP_NAME>,正式修改資料庫schema,並把新的資料填入資料庫:

:::console
$ ./manage.py migrate [APP_NAME]

回復到之前的版本

$ python manage.py migrate photo 0002

查看目前那些版本還沒有做 migrate

$ python manage.py migrate --list

備註

如果發生 django.db.utils.DatabaseError: table "XXXXX" already exists,代表目前資料庫的 schema 跟 south_migrationhistory 裡面所記錄的不同。

可以執行:

:::console 
$ python manage.py migrate dodo --fake

來告訴 south 說,目前資料庫的狀態已經是 migration 資料夾裡面最新的版本了,不需要替我做 migration 的動作。

Python Django

Related Posts

Comments