6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

稼働中のDjango環境でPrimaryKeyをUUIDに変更する(M2Mフィールドも含む)

Last updated at Posted at 2020-04-24

#全体の流れ

  1. uuidフィールドを空のカラムとして追加
  2. 各レコードにuuidを生成
  3. 外部キー制約を削除
  4. ユニークキーを削除
  5. idフィールドを削除
  6. uuidフィールドをidにリネーム
  7. プライマリーキーに設定
  8. 外部キー制約を作成し直す
  9. ユニークキーを作成し直す

#まずは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に変えることができました。
実際は、複数のモデルを変更したため、各工程ごとにマイグレーションファイルを分けて処理を書きました。

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?