\ お問い合わせはこちら! /

【Django】カスタムユーザーモデルの作り方【認証】

  • カスタムユーザーモデルの概念がどうしても理解できない
  • 標準のUserモデルからカスタムユーザーモデルへの変更方法が知りたい

Djangoには標準でログインなどの認証に使えるUserモデルが定義されています。

ところが、残念ながら標準のUserモデルはカスタマイズがほとんどできません

そのため標準のUserモデルにあるフィールドやメソッドでは足りない場合には、独自にユーザーモデルを作る必要があります。

これをカスタムユーザーモデルと言います。

このカスタムユーザーモデルは設定方法がだいぶ煩雑になっているので、本記事で詳しく解説します。

もちろん、プロジェクトの要件がUserモデルで十分ならあえてカスタムユーザーを作成する必要はありません。

【Django】ユーザーモデルの全体像

概略を知っておいた方が理解が進むと思うので、まずはDjangoのユーザーモデルの全体像をご説明します。
» 設定方法だけ知りたいお急ぎの方はこちら

Djangoのユーザーモデルを図式化すると以下ようになります。

一番右にあるUserが、Djangoで標準で用意されているUserモデルです。

そのため、カスタムユーザーを定義するには次の二つのいずれかになります。

  • AbstractUserを継承する
  • AbstractBaseUserとPermissionsMixinを継承する

Django標準のUserモデルと同じものが欲しければAbstractUserだけを継承します。

一方でAbstractUserモデルで定義されているフィールドが不要なら、AbstractBaseUserとPermissionsMixinを継承する流れになります。

以下はカスタムユーザーモデルを作成する場合に絞って、できるだけシンプルに解説します。

AbstractBaseUserの構造

ユーザー認証に必要な基本的なフィールドを提供しています。

AbstractBaseUserが持つフィールド
  • password
  • last_login

PermissionsMixinの構造

ユーザーに権限を付与するためのフィールドが含まれます。

PermissionsMixinが持つフィールド
  • is_superuser
  • groups
  • user_permissions

AbstractUserの構造

DjangoデフォルトのUserモデルフィールドが定義されています。

AbstractUserが持つフィールド
  • username
  • FastAPIirst_name
  • last_name
  • email
  • is_staff
  • is_active
  • date_joined

AbstractUserはAbstractBaseUserとPermissionsMixinを継承しています。

そのため、親クラスが持つ以下のようなフィールドも持っています。

親クラスから継承したフィールド
  • password
  • last_login
  • is_superuser
  • groups
  • user_permissions

Django標準のUserモデルはAbstractUserとほぼ同じ内容と考えていただいて大丈夫だと思います。

【具体的手順】カスタムユーザーモデルの作り方

カスタムユーザーモデルを作成するには、次のステップが必要です。

  1. ユーザー管理用のアプリを作成
  2. モデルを定義
  3. 認証に使うユーザーモデルを変更
  4. 管理画面への適用
  5. モデルのマイグレーション

できるだけ操作やコードが必要な理由も合わせて、詳しく説明していきます。

カスタムユーザー用のアプリを作成

カスタムユーザーモデルを格納するアプリを作成します。

# userアプリの作成
mkdir apps/user
python manage.py startapp user apps/user

ついでにsettings.py内のINSTALLED_APPSにもアプリを追加しておきましょう。

INSTALLED_APPS = [
    # 中略
    "apps.user.apps.UserConfig",
]

appsディレクトリ配下ではなくmanage.pyと同じディレクトリにアプリを配置する場合には、適宜appsを削除して読み替えてください。

カスタムユーザーモデルの作成

カスタムユーザーモデルを定義したmodels.pyは、最終的に以下のような形になります。

【結論】models.py

from django.contrib.auth.models import (
    AbstractBaseUser,
    BaseUserManager,
    PermissionsMixin,
)
from django.db import models
from django.utils import timezone


class CustomUserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError("The Email field must be set")
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)
        return self.create_user(email, password, **extra_fields)


class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    date_joined = models.DateTimeField(default=timezone.now)

    objects = CustomUserManager()

    USERNAME_FIELD = "email"

少し長いので、一つずつ確認していきましょう。

CustomUserクラス

順番は前後しますが、先にCustomUserモデルから説明します。

class CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    date_joined = models.DateTimeField(default=timezone.now)

    USERNAME_FIELD = "email"

上記のようにUSERNAME_FIELDとしてemailを指定すると、メールアドレスとパスワードを使って認証されるようになります。

USERNAME_FIELDで指定されるフィールドは認証に使われるので、必ずunique=Trueとして一意性を確保しておきましょう。

ちなみにDjangoのデフォルトではusernameで認証されます。

CustomUserManagerクラス

CustomUserモデルを修正するため、BaseUserManagerを継承したマネージャークラスを作成します。

おさらいですが、CustomUserManagerは以下のように設定しました。

class CustomUserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        # emailがない場合はエラー
        if not email:
            raise ValueError("The Email field must be set")
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)
        return self.create_user(email, password, **extra_fields)

まず、create_superuser()メソッドが必要な理由からです。

実はこれがないとcreatesuperuserでスーパーユーザーを作っても、管理画面にログインすることができないのです。

理由をより詳しく説明すると、以下になります。

スーパーユーザーが管理画面にログインできない理由
  • 管理画面にログインできるユーザーの条件
    • is_staff: True
    • is_superuser: True
  • カスタムユーザーでcreatesuperuserすると一般ユーザーが作成されてしまう
    • is_staffis_superuserのデフォルトはFalse

そこで、createsuperuserをした時に限ってis_staff, is_superuserTrueにするよう、次のコードを追加しました。

def create_superuser(self, email, password=None, **extra_fields):
    extra_fields.setdefault("is_staff", True)
    extra_fields.setdefault("is_superuser", True)
    return self.create_user(email, password, **extra_fields)

これでcreatesuperuserしたユーザーに限ってはis_staff, is_superuserTrueとなり、無事に管理画面にログインできるようになります。


続いてcreate_user()メソッドです。
このメソッドはユーザーの新規追加で呼びだされます。

内容的にはそこまで難しくはないと思いますがわかりづらい部分を中心に解説します。

def create_user(self, email, password=None, **extra_fields):
    # emailがない場合はエラー
    if not email:
        raise ValueError("The Email field must be set")
    email = self.normalize_email(email)
    user = self.model(email=email, **extra_fields)
    user.set_password(password)
    user.save(using=self._db)
    return user

まずは関数の引数で受け取っている**extra_fieldsについてです。
この引数はemail, passwordフィールド以外のフィールドが辞書で格納されます。

self.normalize_email()はemailを正規化します。正規化とは、例えば@マーク以下のドメイン部分を小文字にするなどですね。

user.set_password()はパスワードをハッシュ化して保存するメソッドです。

最後にuser.save(using=self._db)はデータベースへの保存を表します。
self._dbはsettings.pyでDATABASESとして定義されたデータベースが指定できます。

カスタムユーザーモデルを認証で使うモデルに指定

settings.pyに以下のコードを追記します。

settings.pyに追記

AUTH_USER_MODEL = "user.CustomUser"

上記でDjango標準のUserモデルではなくCustomUserモデルが認証に使われるようになります。

管理画面にカスタムユーザーモデルを反映

カスタムユーザーモデルを管理画面に表示するため、user/admin.pyに以下のコードを記載します。

【結論】admin.py

from django.contrib import admin

from .models import CustomUser


class CustomUserAdmin(admin.ModelAdmin):
    # 一覧画面: 表示項目
    list_display = (
        "email",
        "last_name",
        "first_name",
        "is_staff",
        "is_active",
        "date_joined",
    )
    # 一覧画面: サイドバーフィルター
    list_filter = (
        "email",
        "is_staff",
        "is_active",
    )

    # 一覧画面: 検索ボックス
    search_fields = ("email",)

    # 一覧画面: ソート(降順ならフィールド名の先頭に-)
    ordering = ("email",)

    # 詳細画面: 表示項目
    basic = ("username", "email", "password")
    personal = ("last_name", "first_name", "date_joined")
    auth = ("is_staff", "is_active")

    fieldsets = (
        ("BasicInfo", {"fields": basic}),
        ("Personal", {"fields": personal}),
        ("Auth", {"fields": auth}),
    )


admin.site.register(CustomUser, CustomUserAdmin)

モデルのマイグレーション

最後にマイグレーションを行ってください。

python manage.py makemigrations
python manage.py migrate

以上でカスタムユーザーモデルがDjangoプロジェクトに反映されているはずです。

まとめ

カスタムユーザーモデルを作成するまでの流れは次のとおりでした。

カスタムユーザーモデル作成までの流れ
  1. ユーザー管理用のアプリを作成
  2. モデルを定義
  3. 認証に使うユーザーモデルを変更
  4. 管理画面への適用
  5. モデルのマイグレーション

Userモデルだけを触っているとDjangoの内部構造まで目が行くことがないので、今回の記事は難易度が高い内容だったかもしれません。

ぜひ個人開発などの際に参考にしてみてください。

この記事が気に入ったら
フォローしてね!

シェア・記事の保存はこちら!

この記事を書いた人

karo@プログラマのアバター karo@プログラマ プログラマ

「書くことで人の役にたつ」をモットーに活動中。
本職はプログラマで、Python(Django)が得意。最近ではフロント側の技術に触れる機会も増えてきました。
基本情報技術者試験合格。

コメント

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)