小話〜djangoでCustomUserを実装する際のマイグレーションエラーについてまとめてみた

今回のお題
-
今回は、djangoアプリにCustomUserを実装する上で私が実際に経験したマイグレーションエラーについてまとめます。

どちらもあまり日本語の記事がなかったので、同じような症状でお困りの方のお役に立てばと思います。

目次
-

- エラー１〜AUTH_USER_MODEL refers to model 'accounts.User' that has not been installed

- エラー２〜django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependency user.0001_initial on database 'default'.


エラー１〜AUTH_USER_MODEL refers to model 'accounts.User' that has not been installed
-
このエラーは、私が初めてCustomUserを実装してマイグレーションした際に発生したものになります。

で、私が注目したのが以下の箇所。

```shell
'accounts.User' that has not been installed
```
accounts(ユーザー管理アプリ）がインストールされていません、と書かれているように見えますね。

実際、ネットで調べてもsettings.pyのINSTALLED_APPSを調べるように書かれた記事が山のように出てきます。

しかしsetting.pyは問題なし。

そのまま調べていたところわかったのですが、CustomUserとCustomUserFormを同じファイル(models.py)で定義していると上記のエラーが起きるようです。

```python:CustomUserとCustomFormが同じファイル
class CustomUser(AbstractUser):
  pass

class CustomUserCreationForm(UserCreationForm):
  model = get_user_model()
  ...省略...
```
当たり前と言われるかもしれませんが、models.pyが完全な状態でないとそのアプリケーションはマイグレーションできません。

しかしマイグレーション前であればまだget_user_model()メソッドでCustomUserモデルのインスタンスを取得できる状態ではない、すなわち「models.pyを完成させるためにはマイグレーションが必要だが、models.pyが完成していないのでそもそもマイグレーションができない」という状態になっています。

これを解決するためには諸悪の根源であるmodelFormを別のファイルに移動させるのが最も簡単なので、[前回](https://qiita.com/keishi04hrikzira/items/0c6f6343cce567a00a07)も触れた通りCustomUserとCustomFormはファイルを分けましょうとなっているわけです。

ちなみに私の場合はこれを解決した後に別のエラーに見舞われました。

次項ではそちらについて取り上げます。

エラー２〜django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependency user.0001_initial on database 'default'.
-
長くなるので先に結論から書きます。
#### このエラーは、そのプロジェクトで既に権限ユーザーを作成している場合に発生し、権限ユーザー回りの記述をいくつかコメントアウトすることで解決します。
具体的にコメントアウトするのは以下の箇所です。

```python:settings.py
INSTALLED_APPS = [
   #'django.contrib.admin',
```
```python:urls.py(プロジェクト側）
urlpatterns = [
   #path('admin/', admin.site.urls),
]
```
この二箇所をコメントアウトした上で一度マイグレーションしたのち、コメント部分を元に戻して再度マイグレーションを実行すると無事に解決します。

では、なぜこのようなことが起こるのでしょうか。

エラー文を見てみると次のように書いてありますね。

```shell
Migration admin.0001_initial is applied before its dependency user.0001_initial on database 'default'.
```
和訳すると、
>"admin.0001_initial"が依存先であるuser.0001_initialよりも前にマイグレーションされているよ。

という趣旨のことが書かれています。

そこでadmin.migrations.0001_initial.pyを覗いてみると、以下のような記述がありました。

```python:django.contrib.admin.migrations.0001_initial.py
class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('contenttypes', '__first__'),
    ]

    operations = [
        migrations.CreateModel(
            name='LogEntry',
            fields=[
            # 省略
                ('user', models.ForeignKey(
                    to=settings.AUTH_USER_MODEL,
                    on_delete=models.CASCADE,
                    verbose_name='user',
                )),
            ],
```

前半に出てくる

```python
dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('contenttypes', '__first__'),
    ]
```
はマイグレーションの依存性の定義箇所で、`settings.AUTH_USER_MODEL`に対して依存性があると書かれています。

そしてそのまま少し下に行くと

```python
            fields=[
            # 省略
                ('user', models.ForeignKey(
                    to=settings.AUTH_USER_MODEL,
                    on_delete=models.CASCADE,
                    verbose_name='user',
                )),
```
とあり権限ユーザーはuserを外部キーとするフィールドを持っていることもわかります。

以上をまとめると、外部キーの都合で権限ユーザーのマイグレーションはUserモデルの後に実行されるように設定されているので、既に権限ユーザーがある状態でユーザーモデルを新たに作ろうとするとエラーになるわけですね。

なので設定ファイルを適宜コメントアウトして、権限ユーザー回りのファイルをマイグレーション時の読み込み対象から外しているというわけです。

終わりに
-
以上でマイグレーションエラーに関する小話を終わります。

どちらのエラーも実装手順によってはそもそも遭遇しないものなので関連記事も少なく、解決には若干苦労しました。

同じような状況の方のお役に立てばと思います。