【Django】カスタムユーザーモデル(AbstractUser)を実装する

Django

こんにちは、かつコーチです。

前回の記事ではDjango標準のUserモデルを使ったログイン機能を扱いました。
ただ実務では、標準のUserモデルをそのまま使い続けるプロジェクトはむしろ少数派です。
「メールアドレスでログインさせたい」「電話番号やプロフィール画像を持たせたい」といった要件は、ほぼ確実に発生します。

この記事では、AbstractUserを継承したカスタムユーザーモデルの実装方法と、実務で押さえておくべき判断軸を整理します。
Django 5系を前提に進めます。

なぜAbstractUserによるカスタムユーザーモデルが必要なのか

デフォルトのUserモデルには「あとから変更できない」制約がある

Djangoの公式ドキュメントは、新規プロジェクトでは必ずカスタムユーザーモデルを用意しておくことを強く推奨しています。
理由は明快で、AUTH_USER_MODELはプロジェクトの初回マイグレーション実行前にしか安全に切り替えられないからです。

一度makemigrationsを実行してデータが入った状態からAUTH_USER_MODELを変更しようとすると、既存の外部キー(ForeignKey(User, ...))がすべて新しいモデルへの参照に食い違い、整合性の取れないマイグレーション地獄に陥ります。
つまり、「今は標準のUserで十分だから、必要になったら変える」という判断は事実上できません。

AbstractUserとAbstractBaseUserの選択軸

カスタムユーザーモデルの作り方には2種類あります。

選択肢特徴向いているケース
AbstractUserを継承username, email, is_staffなどの標準フィールドをそのまま引き継ぎ、フィールドを追加するだけで済むユーザー名でのログインは維持しつつ、プロフィール項目を増やしたい場合
AbstractBaseUserを継承標準フィールドを一切持たず、認証周りをすべて自分で設計するユーザー名を廃止して完全にメールアドレスだけで認証したい、独自の権限体系にしたい場合

「とりあえずフィールドを増やしたいだけ」であればAbstractUserで十分です。
ログインの仕組み自体を根本から変えたい場合のみAbstractBaseUserを検討する、という順番で考えると判断がぶれません。

AbstractUserでカスタムユーザーモデルを実装する手順

モデル定義とAUTH_USER_MODELの設定

まずアプリを1つ用意し(ここではaccountsとします)、AbstractUserを継承したモデルを定義します。

# accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models


class CustomUser(AbstractUser):
    """標準のUserにプロフィール項目を追加したカスタムユーザーモデル"""
    phone_number = models.CharField(max_length=15, blank=True)
    is_verified = models.BooleanField(default=False)

    def __str__(self):
        return self.username

続いてsettings.pyAUTH_USER_MODELを追記します。
この1行が抜けていると、Djangoは標準のUserモデルを使い続けてしまいます。

# settings.py
AUTH_USER_MODEL = "accounts.CustomUser"

マイグレーションとDjango Adminへの登録

accountsアプリをINSTALLED_APPSに追加したら、マイグレーションを実行します。

python manage.py makemigrations accounts
python manage.py migrate

管理画面からも編集できるように、UserAdminを継承したカスタム管理クラスを登録しておきます。

# accounts/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser

admin.site.register(CustomUser, UserAdmin)

UserCreationForm・UserChangeFormのカスタム

サインアップ画面や管理画面のフォームで追加フィールドを扱いたい場合は、標準フォームを継承してモデルを差し替えます。

# accounts/forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser


class CustomUserCreationForm(UserCreationForm):
    class Meta(UserCreationForm.Meta):
        model = CustomUser
        fields = UserCreationForm.Meta.fields + ("phone_number",)


class CustomUserChangeForm(UserChangeForm):
    class Meta(UserChangeForm.Meta):
        model = CustomUser
        fields = UserCreationForm.Meta.fields + ("phone_number", "is_verified")

よくあるつまずきポイント

プロジェクト途中でAUTH_USER_MODELを変えようとして詰まった話

筆者は以前、開発の途中段階(本番投入前でしたが、開発DBにテストデータが大量に入った状態)で「あとからメールアドレスでの検索用にフィールドを増やそう」と考え、うっかりAUTH_USER_MODELを新しいアプリのモデルに向け直したことがあります。

結果は、makemigrations実行時に既存のForeignKey(settings.AUTH_USER_MODEL)を持つテーブルすべてで整合性エラーが発生し、開発用DBを作り直す羽目になりました。

django.db.utils.IntegrityError: The row in table 'xxx_xxx' with primary key '1' has an invalid foreign key: xxx_xxx.user_id contains a value 'None' that does not have a corresponding value in auth_customuser.id

このエラーを機に、「カスタムユーザーモデルはstartproject直後、最初のマイグレーション前に必ず入れる」というルールを自分の中に徹底するようになりました。
新規プロジェクトを始めるときは、コーディングを始める前にまずaccountsアプリとAUTH_USER_MODELの設定から着手することを強くおすすめします。

応用・一歩先の使い方

メールアドレスだけでログインさせたい場合

AbstractUserを使いつつ、ユーザー名を使わずメールアドレスでログインさせたい、というケースもよくあります。
その場合はUSERNAME_FIELDを差し替えます。

class CustomUser(AbstractUser):
    email = models.EmailField(unique=True)
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = ["username"]

ただしUSERNAME_FIELDを変更すると認証まわりの前提が大きく変わるため、ここまで踏み込むならAbstractBaseUserから設計した方が結果的にシンプルになるケースも多いです。
「ユーザー名を完全に廃止するのか、残しつつメールも併用するのか」を最初に決めておくと、後戻りが少なくなります。

既存プロジェクトへの後付けは基本的に避ける

繰り返しになりますが、既にデータが入っている本番プロジェクトへのカスタムユーザーモデル導入は、外部キーの張り替えを伴う非常にリスクの高い作業です。
どうしても必要な場合は、データ移行専用のマイグレーションスクリプトを書き、必ずステージング環境で本番相当のデータ量で検証してから臨むべきです。

まとめ

この記事のポイント

  • AUTH_USER_MODELは最初のマイグレーション前にしか安全に変更できないため、新規プロジェクトでは必ずカスタムユーザーモデルを用意する
  • フィールドを追加するだけならAbstractUser、認証の仕組みごと作り直すならAbstractBaseUserを選ぶ
  • 途中導入は外部キーの整合性エラーを引き起こすリスクが高い

次に読むべき記事

認証まわりを固めたら、次はセキュリティ面の理解を深めましょう。
「CSRF対策とDjangoのセキュリティミドルウェアを理解する」の記事で、Djangoのセキュリティ機能を体系的に解説しています。

タグ: Django, 上級者向け, 認証

タイトルとURLをコピーしました