Django migrationのリセット

Djangoであれこれとmodelをいじくってmigrationを繰り返していると、収拾がつかなくなって、最初からmigrationをやり直したい!!
と、なってしまうことがあります。
ここでは、migrationの履歴をリセットし、現在のmodelの内容を正として1からmigrationをやり直す手順を説明します。

環境構成

項目 内容
Django 2.2
DB sqlite3(Django標準)
アプリケーション member_reg
テーブル member_reg_register

1.modelを変更する。

「member_reg」はユーザー登録アプリケーションです。modelはRegisterとしています。
元々はPrimary key項目を設定していなかったのですが、新たに「user_id」という項目をPrimary keyとして追加します。
DjangoではPrimary keyを指定しないmodelは「id」という項目名でPrimary keyが自動で設定されています。

models.py(抜粋)
class Register(models.Model):
    user_id = models.AutoField(primary_key=True)
    family_name = models.CharField(max_length=50)
    first_name = models.CharField(max_length=50)
    family_name_k = models.CharField(max_length=50)
    first_name_k = models.CharField(max_length=50)
    mail = models.EmailField(max_length=100)
    password = models.CharField(max_length=50)
    tel1_1 = models.CharField(max_length=10)
    tel1_2 = models.CharField(max_length=10)
    tel1_3 = models.CharField(max_length=10)
    tel2_1 = models.CharField(max_length=10)
    tel2_2 = models.CharField(max_length=10)
    tel2_3 = models.CharField(max_length=50)
    gender = models.CharField(max_length=1)
    birthday = models.DateField()
    post_num = models.CharField(max_length=7)
    prefecture = models.CharField(max_length=30)
    city = models.CharField(max_length=100)
    town = models.CharField(max_length=100)
    banchi1 = models.CharField(max_length=5)
    banchi2 = models.CharField(max_length=5)
    banchi3 = models.CharField(max_length=5)
    place = models.CharField(max_length=50)

2.mikemigrationの実行

user_idはnot null項目なので何か値を入れてくれとの警告がでます。ここではとりあえず0を設定します。

(django) C:\django\myprj>python manage.py makemigrations
You are trying to add a non-nullable field 'user_id' to register without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> 0
Migrations for 'member_reg':
member_reg\migrations\0009_auto_20200507_0050.py
- Remove field id from register
- Add field user_id to register

makemigration自体は動いていますが、failedで終わっています。
このままmigrateすると、以下のエラーになります。
django.db.utils.IntegrityError: UNIQUE constraint failed: new__member_reg_register.user_id
当然Primary keyの項目の値が重複してしまいます。
色々とやり方はあるようですが、過去のmigrationの履歴をリセットして新たにmigrationを開始してみます。

3.mikemigrationのリセット

まずは先ほどの状態からmakemigrationsします。
No changes detectedとなっていればOK.

(django) C:\django\myprj>python manage.py makemigrations
No changes detected

次にmigrationの履歴を確認します。
[X]はmigrate済、[ ]はmigrate未です。0009_auto_20200507_0050はmigrate出来ていません。

(django) C:\django\myprj>python manage.py showmigrations
admin
[X] 0001_initial
[X] 0002_logentry_remove_auto_add
[X] 0003_logentry_add_action_flag_choices
auth
[X] 0001_initial
[X] 0002_alter_permission_name_max_length
[X] 0003_alter_user_email_max_length
[X] 0004_alter_user_username_opts
[X] 0005_alter_user_last_login_null
[X] 0006_require_contenttypes_0002
[X] 0007_alter_validators_add_error_messages
[X] 0008_alter_user_username_max_length
[X] 0009_alter_user_last_name_max_length
[X] 0010_alter_group_name_max_length
[X] 0011_update_proxy_permissions
contenttypes
[X] 0001_initial
[X] 0002_remove_content_type_name
jsonsample
(no migrations)
member_reg
[X] 0001_initial
[X] 0002_auto_20200115_0051
[X] 0003_auto_20200119_1952
[X] 0004_auto_20200119_2039
[X] 0005_auto_20200119_2059
[X] 0006_auto_20200119_2245
[X] 0007_auto_20200123_0102
[X] 0008_auto_20200310_0013
[ ] 0009_auto_20200507_0050
modaltest
[X] 0001_initial
[X] 0002_auto_20200411_0956
sessions
[X] 0001_initial

–fake オプション、zeroオプションを指定して、擬似的にmigrationを削除します。

(django) C:\django\myprj>python manage.py migrate --fake member_reg zero
Operations to perform:
Unapply all migrations: member_reg
Running migrations:
Rendering model states... DONE
Unapplying member_reg.0008_auto_20200310_0013... FAKED
Unapplying member_reg.0007_auto_20200123_0102... FAKED
Unapplying member_reg.0006_auto_20200119_2245... FAKED
Unapplying member_reg.0005_auto_20200119_2059... FAKED
Unapplying member_reg.0004_auto_20200119_2039... FAKED
Unapplying member_reg.0003_auto_20200119_1952... FAKED
Unapplying member_reg.0002_auto_20200115_0051... FAKED
Unapplying member_reg.0001_initial... FAKED

ここでもう一度showmigrationsします。
member_regに関するmigrationの履歴が全て削除されています。

(django) C:\django\myprj>python manage.py showmigrations
admin
[X] 0001_initial
[X] 0002_logentry_remove_auto_add
[X] 0003_logentry_add_action_flag_choices
auth
[X] 0001_initial
[X] 0002_alter_permission_name_max_length
[X] 0003_alter_user_email_max_length
[X] 0004_alter_user_username_opts
[X] 0005_alter_user_last_login_null
[X] 0006_require_contenttypes_0002
[X] 0007_alter_validators_add_error_messages
[X] 0008_alter_user_username_max_length
[X] 0009_alter_user_last_name_max_length
[X] 0010_alter_group_name_max_length
[X] 0011_update_proxy_permissions
contenttypes
[X] 0001_initial
[X] 0002_remove_content_type_name
jsonsample
(no migrations)
member_reg
[ ] 0001_initial
[ ] 0002_auto_20200115_0051
[ ] 0003_auto_20200119_1952
[ ] 0004_auto_20200119_2039
[ ] 0005_auto_20200119_2059
[ ] 0006_auto_20200119_2245
[ ] 0007_auto_20200123_0102
[ ] 0008_auto_20200310_0013
[ ] 0009_auto_20200507_0050
modaltest
[X] 0001_initial
[X] 0002_auto_20200411_0956
sessions
[X] 0001_initial

migrationsファイルを削除します。
migrationsは以下のフォルダから「__ init__.py」を除く全てのファイルを削除します。
サブフォルダもすべて対象です。

残るのはこれだけになるはずです。
__pycache__
__init__.py

ここでもう一度showmigrationsを実行します。

(django) C:\django\myprj>python manage.py showmigrations
admin
[X] 0001_initial
[X] 0002_logentry_remove_auto_add
[X] 0003_logentry_add_action_flag_choices
auth
[X] 0001_initial
[X] 0002_alter_permission_name_max_length
[X] 0003_alter_user_email_max_length
[X] 0004_alter_user_username_opts
[X] 0005_alter_user_last_login_null
[X] 0006_require_contenttypes_0002
[X] 0007_alter_validators_add_error_messages
[X] 0008_alter_user_username_max_length
[X] 0009_alter_user_last_name_max_length
[X] 0010_alter_group_name_max_length
[X] 0011_update_proxy_permissions
contenttypes
[X] 0001_initial
[X] 0002_remove_content_type_name
jsonsample
(no migrations)
member_reg
(no migrations)
modaltest
[X] 0001_initial
[X] 0002_auto_20200411_0956
sessions
[X] 0001_initial
member_reは「(no migrations)」になっています。

4.mikemigrationの再実行

過去のmigration履歴は削除できましたので、ここからが新規のmigrationです。

(django) C:\django\myprj>python manage.py makemigrations
Migrations for 'member_reg':
member_reg\migrations\0001_initial.py
- Create model Register

makemigrationはうまくいきました。

(django) C:\django\myprj>python manage.py migrate
django.db.utils.OperationalError: table "member_reg_register" already exists

migrateは実テーブルがすでに存在するので怒られてしまいます。

データベースを削除します。sqlite3なのでコマンドで削除していきます。

(django) C:\django\myprj> python manage.py dbshell
SQLite version 3.28.0 2019-04-16 19:49:53
Enter ".help" for usage hints.
sqlite> .tables
auth_group django_admin_log
auth_group_permissions django_content_type
auth_permission django_migrations
auth_user django_session
auth_user_groups member_reg_register
auth_user_user_permissions modaltest_condition
sqlite>

DROP文を発行して、テーブルを削除します。

sqlite> drop table member_reg_register;
sqlite> .tables
auth_group django_admin_log
auth_group_permissions django_content_type
auth_permission django_migrations
auth_user django_session
auth_user_groups modaltest_condition
auth_user_user_permissions
sqlite>

この状態でもう一度migrationにトライしてみます。

(django) C:\django\myprj> python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, member_reg, modaltest, sessions
Running migrations:
Applying member_reg.0001_initial... OK

成功しました。

migrationの結果を見てみます。
「member_reg」は「 [X] 0001_initial」の1回のみです。

(django) C:\django\myprj> python manage.py showmigrations
admin
[X] 0001_initial
[X] 0002_logentry_remove_auto_add
[X] 0003_logentry_add_action_flag_choices
auth
[X] 0001_initial
[X] 0002_alter_permission_name_max_length
[X] 0003_alter_user_email_max_length
[X] 0004_alter_user_username_opts
[X] 0005_alter_user_last_login_null
[X] 0006_require_contenttypes_0002
[X] 0007_alter_validators_add_error_messages
[X] 0008_alter_user_username_max_length
[X] 0009_alter_user_last_name_max_length
[X] 0010_alter_group_name_max_length
[X] 0011_update_proxy_permissions
contenttypes
[X] 0001_initial
[X] 0002_remove_content_type_name
jsonsample
(no migrations)
member_reg
[X] 0001_initial
modaltest
[X] 0001_initial
[X] 0002_auto_20200411_0956
sessions
[X] 0001_initial

テーブルも確認できます。

(django) C:\django\myprj>python manage.py dbshell
SQLite version 3.28.0 2019-04-16 19:49:53
Enter ".help" for usage hints.
sqlite> .tables
auth_group django_admin_log
auth_group_permissions django_content_type
auth_permission django_migrations
auth_user django_session
auth_user_groups member_reg_register
auth_user_user_permissions modaltest_condition

user_idがprimary keyになっています。

sqlite> .schema member_reg_register
CREATE TABLE IF NOT EXISTS "member_reg_register" (
"user_id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"family_name" varchar(50) NOT NULL,
"first_name" varchar(50) NOT NULL,
"family_name_k" varchar(50) NOT NULL,
"first_name_k" varchar(50) NOT NULL,
"mail" varchar(100) NOT NULL,
"password" varchar(50) NOT NULL,
"tel1_1" varchar(10) NOT NULL,
"tel1_2" varchar(10) NOT NULL,
"tel1_3" varchar(10) NOT NULL,
"tel2_1" varchar(10) NOT NULL,
"tel2_2" varchar(10) NOT NULL,
"tel2_3" varchar(50)NOT NULL,
"gender" varchar(1) NOT NULL,
"birthday" date NOT NULL,
"post_num" varchar(7) NOT NULL,
"prefecture" varchar(30) NOT NULL,
"city" varchar(100) NOT NULL,
"town" varchar(100) NOT NULL,
"banchi1" varchar(5) NOT NULL,
"banchi2" varchar(5) NOT NULL,
"banchi3" varchar(5) NOT NULL,
"place" varchar(50) NOT NULL);
sqlite>