一般在 Django 底下更動 model 的時侯,必須使用 syncdb 這個指令,把修改的 model 同步到 database 之中,但是有個限制,就是只有新的 model 才會被更新,已經存在的 model 就不會更動,此時就需要south的幫忙。在 django 1.7 之後,這個 library 正式被整進到 django 之中。不過目前我手頭上的專案還是以 1.6 為主,所以還是要筆記一下。
初始化 south
安裝完 south 之後,第一次先執行 syncdb 把 south 相關的south_migrationhistory 新增到資料庫中。
$ ./manage.py syncdb
開始 migration
首先要介紹的是 schemamigration <APP_NAME> --initial,這個指令會在 app 資料夾下面產生一個 migrations 資料夾。如果是第一次執行的話,裡面的檔名會是 0001_initial.py 開頭,裡面會記錄如何從無到有,把資料表建立起來。
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 做一下確認
$ 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 資料夾的那一個版本。
$ python manage.py migrate rango 0001 --fake
另一個情況是我們沒有對這個 app 同步過,這時就不能用 syncdb 來做,要改用
$ 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 資料夾中。
$ ./manage.py schemamigration rango --auto
過程當中會詢問你如果有新的欄位,是否要填入一預設值。
最後再使用migrate <APP_NAME>,正式修改資料庫schema,並把新的資料填入資料庫:
$ ./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 裡面所記錄的不同。
可以執行:
$ python manage.py migrate dodo --fake
來告訴 south 說,目前資料庫的狀態已經是 migration 資料夾裡面最新的版本了,不需要替我做 migration 的動作。