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

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

#まずはidフィールドをオーバーライド
ベースモデルクラスがあれば、ベースモデルクラスに
なければ各モデルクラスにidをUUIDFieldで定義

```python
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
```

#マイグレーションファイル作成
```bash
python manage.py makemigration
```
###生成されたマイグレーションファイルを修正
```python

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フィールドがあるとします。

```SQL
SHOW CREATE TABLE users_myuser;
```
あたらしくmakemigration --emptyでファイルを作るか、上の配列に追加

```python
        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に変換

```python
        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に変換

```python
        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),
        ),  
```

#最後に外部キー制約、ユニークキーを貼り直す

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