這一節(jié)介紹你可能遇到的在不同情況下如何分析和編寫數(shù)據(jù)庫遷移. 有關(guān)遷移的入門資料,請查看 the topic guide.
在使用多個數(shù)據(jù)庫時,需要解決是否針對某個特定數(shù)據(jù)庫運行遷移。例如,你可能 只 想在某個特定數(shù)據(jù)庫上運行遷移。
為此你可以在RunPython中通過查看schema_editor.connection.alias 屬性來檢查數(shù)據(jù)庫連接別名:
from django.db import migrations
def forwards(apps, schema_editor):
if not schema_editor.connection.alias == 'default':
return
# Your migration code goes here
class Migration(migrations.Migration):
dependencies = [
# Dependencies to other migrations
]
operations = [
migrations.RunPython(forwards),
]
Django 1.8 中新增。
你也可以提供一個提示作為 **hints參數(shù)傳遞到數(shù)據(jù)庫路由的allow_migrate() 方法:
myapp/dbrouters.py
class MyRouter(object):
def allow_migrate(self, db, app_label, model_name=None, **hints):
if 'target_db' in hints:
return db == hints['target_db']
return True
然后,要在你的遷移中利用,執(zhí)行以下操作:
from django.db import migrations
def forwards(apps, schema_editor):
# Your migration code goes here
class Migration(migrations.Migration):
dependencies = [
# Dependencies to other migrations
]
operations = [
migrations.RunPython(forwards, hints={'target_db': 'default'}),
]
如果你的RunPython或者RunSQL操作只對一個模型有影響,最佳實踐是將model_name作為提示傳遞,使其盡可能對路由可見。這對可復(fù)用的和第三方應(yīng)用極其重要。
如果你應(yīng)用了一個“樸素”的遷移,向表中一個已存在的行中添加了一個唯一的非空字段,會產(chǎn)生錯誤,因為位于已存在行中的值只會生成一次。所以需要移除唯一性的約束。
所以,應(yīng)該執(zhí)行下面的步驟。在這個例子中,我們會以默認(rèn)值添加一個非空的UUIDField字段。你可以根據(jù)你的需要修改各個字段。
生成的遷移類看上去像這樣:
class Migration(migrations.Migration):
dependencies = [
('myapp', '0003_auto_20150129_1705'),
]
operations = [
migrations.AddField(
model_name='mymodel',
name='uuid',
field=models.UUIDField(max_length=32, unique=True, default=uuid.uuid4),
),
]
你需要做三處更改:
最終的遷移類應(yīng)該看起來是這樣:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import uuid
def gen_uuid(apps, schema_editor):
MyModel = apps.get_model('myapp', 'MyModel')
for row in MyModel.objects.all():
row.uuid = uuid.uuid4()
row.save()
class Migration(migrations.Migration):
dependencies = [
('myapp', '0003_auto_20150129_1705'),
]
operations = [
migrations.AddField(
model_name='mymodel',
name='uuid',
field=models.UUIDField(default=uuid.uuid4, null=True),
),
# omit reverse_code=... if you don't want the migration to be reversible.
migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
migrations.AlterField(
model_name='mymodel',
name='uuid',
field=models.UUIDField(default=uuid.uuid4, unique=True),
),
]
現(xiàn)在你可以像平常一樣使用migrate命令應(yīng)用遷移。
注意如果你在這個遷移運行時讓對象被創(chuàng)建,就會產(chǎn)生競爭條件(race condition)。在AddField之后, RunPython之前創(chuàng)建的對象會覆寫他們原始的uuid。