【Django】独自ユーザーモデルの作成(カスタムUserモデル:AbstractBaseUser)

2021.12.14 /

【Django】独自ユーザーモデルの作成(カスタムUserモデル:AbstractBaseUser)

本記事ではDjango独自のユーザーモデルを作成する方法について解説していきます。

Djangoには標準でUserモデルが用意されています。しかしほとんどの場合で作成するWebアプリケーションに合わせてUserモデルをカスタマイズする必要があります。

カスタムUserモデルを作成する方法はいくつかありますが、ここでは最も自由度が高いAbstractBaseUserを継承して作成する方法について詳しく解説していきます。

デフォルト(標準)のUserモデル

Djangoには標準で利用できるUserモデルが用意されています。このUserモデルはdjango.contrib.auth.models.Userに次のように定義されています。

class User(AbstractUser):
    class Meta(AbstractUser.Meta):
        swappable = 'AUTH_USER_MODEL'

標準UserモデルはAbstractUserを継承していることがわかります。
そのため標準Userモデルでは以下に示すAbstractUserで定義されているカラムが定義されます。

username = models.CharField()
first_name = models.CharField()
last_name = models.CharField()
email = models.EmailField()
is_staff = models.BooleanField()
is_active = models.BooleanField()
date_joined = models.DateTimeField()
カラム名 説明
username ユーザー名
first_name 名前
last_name 苗字
email メールアドレス
is_staff 管理画面のアクセス可否
is_active ログインの可否
date_joined アカウントの作成日時

Djangoのチュートリアルなどでは、この標準Userモデルを利用して開発を進めることが多いです。

カスタムUserモデルの作成

カスタムUserモデルを作成する理由

Djangoの標準Userモデルはインストール後すぐに利用することができるため、Django初心者の方が利用するにはとても便利です。

しかし独自のWebアプリケーションを作成する場合、ユーザー情報に必要な項目は標準Userモデルと異なるのが普通です。
またログインにusernameではなくemailを利用したい場合もあります。

そのため大抵の場合、UserモデルをWebアプリケーションに合わせてカスタマイズする必要があるのです。

django公式サイトでも、カスタムユーザーモデルを利用することを強く推奨しています。

カスタムUserモデルの作成方法

ユーザーモデルをカスタマイズする方法は以下3つの方法があります。

  • 標準Userモデルと1対1の関係を持つモデルを作成
  • AbstractUserを継承したモデルを作成
  • AbstractBaseUserを継承したモデルを作成

標準Userモデルと1対1の関係を持つモデルを作成

例えば次のように標準Userモデルと1対1の関係を持つモデルを作成し、ユーザー情報に追加したいフィールドをそこに書き込みます。

class UserAddInfo(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    department = models.CharField()
    image = models.ImageField()

このように標準のUserモデルを活かしたまま、新しい情報をUserモデルに追加できます。

しかしこの方法では既存のカラム(first_nameやlast_nameなど)を必ず利用しなければならないですし、ログインをemailに変更することもできないです。

またテーブルが複数に分かれることにより、Webアプリケーションのパフォーマンスが悪くなることが考えられます。

これらの理由より、標準Userモデルと1対1の関係を持つモデルを作成することはお勧めしません。

AbstractUserを継承したモデルを作成

AbstractUserは標準Userモデルが継承して利用しているモデルです。

AbstractUserモデルはdjango.contrib.auth.modelsで以下のように定義されています。

class AbstractUser(AbstractBaseUser, PermissionsMixin):
    username_validator = UnicodeUsernameValidator()
    username = models.CharField(_('username'), max_length=150, unique=True,…)
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=150, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(_('staff status'), default=False,…)
    is_active = models.BooleanField(_('active'), default=True,…)
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = UserManager()

    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        abstract = True

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

上記AbstractUserモデルを継承してカスタムUserモデルを作成するため、「標準Userモデルと1対1の関係を持つモデルを作成」と大きな違いはないです。

テーブルが複数に分かれることはないので、パフォーマンス面で不利になることはありません。しかしカラム(属性)の追加しかできないのでモデルの柔軟性は低いです。

標準Userモデルにカラム(フィールド)の追加のみしたい場合はこちらの方法を利用します。

AbstractBaseUserを継承したモデルを作成

AbstractBaseUserはdjango.contrib.auth.base_userで以下のように定義されています。

class AbstractBaseUser(models.Model):
    password = models.CharField(_('password'), max_length=128)
    last_login = models.DateTimeField(_('last login'), blank=True, null=True)

    is_active = True

    REQUIRED_FIELDS = []

    _password = None

    class Meta:
        abstract = True

AbstractBaseUserはdjango.db.models.Modelを継承しています。

AbstractBaseUserには認証機能のみが含まれており、独自でフィールドを設定することができます。

つまり認証以外の部分はなにも実装されていないクラスであり、first_nameやlast_nameといった必要のないフィールドをUserモデルで使わないで済むのです。

AbstractBaseUserを継承したUserモデルは作成が少々面倒ではありますが、カスタマイズの柔軟性は非常に高いです。

ここまでのモデルの継承関係は次図のようになります。

Django:モデルUserの継承関係

AbstractBaseUserを継承したUserモデルの作成

ここではカスタマイズの柔軟性が高く、自由度があるAbstractBaseUserを継承したUserモデルの作成方法を解説していきます。

カスタムUserモデルは以下の流れで作成していきます。

  1. アプリケーションの生成
  2. カスタムUserモデルの作成
  3. カスタムUserManagerの作成
  4. 認証モデルの変更
  5. マイグレーションの実行
  6. 管理画面の設定
注意点

カスタムUserモデルを利用する場合は、Djangoプロジェクトを作成後すぐに設定する必要があります。migrateを実行した後ではUserモデルの変更は非常に難しいです

またここで作成するUserモデルは、ログイン認証にユーザー名ではなく、メールアドレスを利用するように変更します。

1.アプリケーションの生成

まずはユーザー管理用のアプリケーションを作成します。manage.py startappコマンドでusersアプリケーションを作成しましょう。

python manage.py startapp users

プロジェクトフォルダ内のsettings.pyのINSTALLED_APPS欄に生成したアプリケーションを追加します。

INSTALLED_APPS = [
    '…',
    'users.apps.UsersConfig',
]

2.カスタムUserモデルの作成

次にカスタムUserモデルを作成していきます。

作成したアプリケーション内のmodels.pyを以下のように変更します。django.contrib.auth.models.AbstractUserを元にして作成しています。

from django.db import models
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.core.mail import send_mail
from django.utils import timezone
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.utils.translation import gettext_lazy as _

class User(AbstractBaseUser, PermissionsMixin):
    username_validator = UnicodeUsernameValidator()

    username = models.CharField(_("username"), max_length=50, validators=[username_validator], blank=True)
    email = models.EmailField(_("email_address"), unique=True)
    is_staff = models.BooleanField(_("staff status"), default=False)
    is_active = models.BooleanField(_("active"), default=True)
    date_joined = models.DateTimeField(_("date joined"), default=timezone.now)

    objects = UserManager()
    USERNAME_FIELD = "email"
    EMAIL_FIELD = "email"
    REQUIRED_FIELDS = ['username']

    class Meta:
        verbose_name = _("user")
        verbose_name_plural = _("users")

    def clean(self):
        super().clean()
        self.email = self.__class__.objects.normalize_email(self.email)

    def email_user(self, subject, message, from_email=None, **kwargs):
        send_mail(subject, message, from_email, [self.email], **kwargs)

objects

Userモデルで設定しているobjects変数は、views.pyでUserモデルの情報を取得する際などで利用します。

user = User.objects.get(username="office54")

objects変数に指定するクラス(ここではUserManager())は、ターミナルでユーザーを作成(manage.py createsuperuserなど)する際に呼ばれます。

USERNAME_FIELD

USERNAME_FIELDで指定したフィールドは、ログイン認証やメール送信などで利用します。

ここをemailにすることでメールアドレスでのログインが可能になります。

指定したフィールドは一意である必要があるため、ここで指定できるフィールドはunique=Trueである必要があります。

EMAIL_FIELD

ターミナルでユーザー作成(manage.py createsuperuser)するときに表示される項目です。
ユーザーは指定した項目の値を入力するよう求められます。

複数のフィールドを設定することができます。

3.カスタムUserManagerの作成

ターミナルでユーザーを作成(manage.py createsuperuserなど)する際に呼ばれるUserManagerを、カスタムUserモデル用に書き換えます。

UserManagerクラスには_create_user関数とcreate_user関数、create_superuserの3つがあります。それぞれユーザーを新規で作成する際に呼ばれる関数です。

class User(AbstractBaseUser, PermissionsMixin)の上部に以下を追記します。

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, username, email, password, **extra_fields):
        if not email:
            raise ValueError('Emailを入力して下さい')
        email = self.normalize_email(email)
        username = self.model.normalize_username(username)
        user = self.model(username=username, email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self.db)
        return user
    def create_user(self, username, email, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, username, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        if extra_fields.get('is_staff') is not True:
            raise ValueError('is_staff=Trueである必要があります。')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('is_superuser=Trueである必要があります。')
        return self._create_user(username, email, password, **extra_fields)

emailは必須項目なので、emailが空の場合は例外が発生するようにしています。

4.認証モデルの変更

カスタムUserモデルを認証機能で使用する認証モデルに変更します。

以下のようにsettings.pyにAUTH_USER_MODELにアプリケーション名.モデル名を指定した設定を追加します。

AUTH_USER_MODEL = 'users.User'

5.マイグレーションの実行

マイグレーションを実行して、作成したカスタムUserモデルをデータベースに反映させます。

python manage.py migrations
python manage.py migrate

6.管理サイトの設定

管理サイトでカスタムUserモデルを利用できるようにするため、admin.pyを以下のように変更します。

またUserCreationFormやUserChangeFormもそのまま利用することができないため、専用フォームを用意しています。

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from django.utils.translation import ugettext_lazy as _
from .models import User

class MyUserChangeForm(UserChangeForm):
    class Meta:
        model = User
        fields = '__all__'

class MyUserCreationForm(UserCreationForm):
    class Meta:
        model = User
        fields = ('email','username')

class MyUserAdmin(UserAdmin):
    fieldsets = (
        (None, {'fields': ('email', 'password', 'username')}),
        (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
                                       'groups', 'user_permissions')}),
        (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
    )
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'password1', 'password2'),
        }),
    )
    form = MyUserChangeForm
    add_form = MyUserCreationForm
    list_display = ('email', 'username', 'is_staff')
    list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups')
    search_fields = ('email', 'username')
    ordering = ('email',)

admin.site.register(User, MyUserAdmin)

まとめ

本記事「【Django】独自ユーザーモデルの作成(カスタムUserモデル:AbstractBaseUser)」はいかがでしたか。

標準で用意されているUserモデルでは自由にWebアプリケーションが作成できません。

柔軟にカスタマイズができるカスタムUserモデルを作れるようにして、作成できるWebアプリケーションの幅を広げてください。