#全体の流れ
- uuidフィールドを空のカラムとして追加
- 各レコードにuuidを生成
- 外部キー制約を削除
- ユニークキーを削除
- idフィールドを削除
- uuidフィールドをidにリネーム
- プライマリーキーに設定
- 外部キー制約を作成し直す
- ユニークキーを作成し直す
#まずはidフィールドをオーバーライド
ベースモデルクラスがあれば、ベースモデルクラスに
なければ各モデルクラスにidをUUIDFieldで定義
from django.db import models
import uuid
class SuperModel(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
objects = SuperModelManager()
deleted = models.BooleanField(u'削除', default=False)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
#マイグレーションファイル作成
python manage.py makemigration
###生成されたマイグレーションファイルを修正
def fill_uuid_user(apps, schema_editor):
db_alias = schema_editor.connection.alias
MyModel = apps.get_model('users', 'myuser')
for obj in MyModel.objects.using(db_alias).all():
obj.uuid = uuid.uuid4()
obj.save()
class Migration(migrations.Migration):
dependencies = [
('users', '0001_auto_20200409_1138'),
]
operations = [
# 生成されたコードはコメントアウト
# migrations.AlterField(
# model_name='myuser',
# name='id',
# field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
# ),
# uuidという名前で非ユニークですべてnullでいったん作成
migrations.AddField(
model_name='myuser',
name='uuid',
field=models.UUIDField(null=True),
),
# UUID生成(こうしないと一つのUUIDをすべてのレコードに入れようとする)
migrations.RunPython(fill_uuid_myuser, migrations.RunPython.noop),
# ユニーク設定、まだプライマリーキーにはしない.
migrations.AlterField(
model_name='myuser',
name='uuid',
field=models.UUIDField(default=uuid.uuid4, serialize=False, editable=False, unique=True),
),
]
#さらにマイグレーションファイルを変更
DBに接続して以下のコマンドで確認できる外部キー制約とユニークキーをいったん削除します。
以下の例ではmymodelモデルにitemというForeignKeyフィールドがあるとします。
SHOW CREATE TABLE users_myuser;
あたらしくmakemigration --emptyでファイルを作るか、上の配列に追加
migrations.RunSQL(
"""
ALTER TABLE users_myuser DROP FOREIGN KEY users_myuser_id_c564eba6_fk_items_item_id;
ALTER TABLE users_myuser DROP INDEX email;
"""
),
#モデルからForeignKeyで参照されているidをuuidに変換
migrations.RunSQL(
"""
ALTER TABLE users_myuser ADD item_id_uuid char(32);
UPDATE users_myuser t1, items_item t2 SET t1.item_id_uuid = t2.uuid WHERE t1.item_id = t2.id;
ALTER TABLE users_myuser DROP COLUMN item_id;
ALTER TABLE users_myuser CHANGE item_id_uuid item_id char(32);
ALTER TABLE users_myuser MODIFY COLUMN item_id char(32);
)
#モデルのidを削除して、uuidに変換
migrations.RemoveField('myuser', 'id'),
migrations.RenameField(
model_name='myuser',
old_name='uuid',
new_name='id'
),
migrations.AlterField(
model_name='myuser',
name='id',
field=models.UUIDField(primary_key=True, default=uuid.uuid4, serialize=False, editable=False, unique=True),
),
#最後に外部キー制約、ユニークキーを貼り直す
migrations.RunSQL(
"""
ALTER TABLE users_myuser ADD CONSTRAINT users_myuser_id_c564eba6_fk_items_item_id FOREIGN KEY (item_id) REFERENCES items_item(id);
ALTER TABLE users_myuser ADD UNIQUE email (email);
"""
)
#おわり
これらの処理が書かれたマイグレーションファイルでマイグレートを実行すれば無事、idからuuidに変えることができました。
実際は、複数のモデルを変更したため、各工程ごとにマイグレーションファイルを分けて処理を書きました。