こんにちは、かつコーチです。
前回の記事では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.pyにAUTH_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, 上級者向け, 認証