【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractBaseUser編】

カスタムユーザーをAbstractBaseUserを継承して作成する手順の解説ページアイキャッチ

このページでは、Django での「カスタムユーザー」の作り方について解説していきます。

本サイトにおいては、別のカスタムユーザーの作り方として、下記ページで AbstractUser 編として解説を行なっています。

【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】

上記ページでは AbstractUser を継承してカスタムユーザーを作成しましたが、このページでは AbstractBaseUser(さらには PermissionsMixin)を継承してカスタムユーザーを作成する手順を解説していきます。

特にこのページでは「カスタムユーザーの作り方」のみに焦点を当てて解説を行なっていきたいと思っています。作ったカスタムユーザーをアプリで使用する手順については、上記ページの AbstractUser を継承して作成する場合とほぼ同様なので、このページでは詳細な説明は省略させていただきます(一応このページでも最後にカスタムユーザーをアプリで使用する実装例を紹介します)。

なので、作成したカスタムユーザーをアプリでどうやって利用すれば良いのか?については、上記ページを参考にしていただければと思います。

AbstractBaseUser

最初に AbstractBaseUser について解説をしておきます。

AbstractBaseUser と関連クラスとの関係

まず、AbstractBaseUser や AbstractUser 等の関係を図で表すと下図のようになります。

AbstractBaseUserと関連モデルの関係図

この図からも分かるように、AbstractUser は AbstractBaseUser と、さらには PermissionsMixin というクラスを継承して作成されているモデルになります。

さらに、Django でデフォルトで用意されている User は、ほぼ AbstractUser を継承するのみで作成されているモデルになります。

AbstractUser を継承してカスタムユーザーを作成した場合、当然 AbstractUser の持つフィールドやメソッドもカスタムユーザーに継承されるため、開発するアプリによっては不要なものまで継承される可能性があります。

AbstractUser は一般的なユーザーの情報を管理するためのフィールドを持っており、例えば first_namelast_name というフィールドを持っていますが、アプリで first_namelast_name が不要という場合も多いのではないかと思います。

AbstractUser を継承してカスタムユーザーを作成した場合、そういった不要なフィールドもカスタムユーザーに継承されることになり、開発するアプリによってはそういったフィールドが邪魔になる可能性もあります。

AbstractUserを継承することで不要なフィールドまで継承されてしまうことを示す図

AbstractBaseUser を継承するメリット

それに対し、AbstractBaseUserPermissionsMixin にはユーザーを管理する上で必要最低限なフィールドやメソッドしか持っていません。例えば認証関連や権限管理関連のフィールドやメソッドのみを持つクラスとなります。

そのため、これらを継承してカスタムユーザーを作成した場合は、基本的には不要なフィールドやメソッドが継承されないことになります(もし認証や権限管理を行わないのであれば、継承されるフィールドやメソッドは不要ということになりますが…)。

なので、継承して定義したモデルに、自身で好きなフィールドやメソッドのみを追加していくことで、アプリにとって必要な情報のみを管理するカスタムユーザーを作成することが出来ます。

AbstractBaseUserを継承することで不要なフィールドの継承を防ぐことができることを示す図

そのため、AbstractUser を継承するよりも、AbstractBaseUserPermissionsMixin を継承した方が、作成できるカスタムユーザーの自由度が高いというメリットがあります。

ただ、自由度が高い分、AbstractBaseUserPermissionsMixin を継承してカスタムユーザーを作成する方が難易度は高いと思います。

この理由は、Django フレームワークのクラスの中には、カスタムユーザーに特定のフィールドが存在することを前提に作成されているものがあるからです。

分かりやすいのが username フィールドです。例えば、作成するカスタムユーザーに username フィールドを持たせない場合、username が存在することを前提として作成されているクラスも含めて変更する必要があります。もしくは不要であっても username フィールドをカスタムユーザーに持たせるようなことが必要になります。

フィールドを削除する際に他のクラスの変更も必要になることを示す図

そのため、AbstractBaseUserPermissionsMixin を継承してカスタムユーザーを作成する場合であっても、一旦 AbstractUser と同等のカスタムユーザーを定義して他のクラスへの影響を最小限に抑え、その上で、定義したカスタムユーザーに対して不要なフィールドを削除したり、必要なフィールドを追加してカスタマイズしていく手順を取るのが無難だと思います。

このあたりも含めて、ここから AbstractBaseUserPermissionsMixin を継承してカスタムユーザーを作成する手順を解説していきます。

カスタムユーザーを定義する

では、AbstractBaseUser および、PermissionsMixin を継承してカスタムユーザーを作成する手順について解説していきます。

AbstractBaseUser および PermissionsMixin を継承してカスタムユーザーを作成していく場合、前述の通り、一旦 AbstractUserAbstractBaseUser ではなく)の定義を models.py にコピペしてカスタムユーザーモデルとして定義し、不要なフィールドやメソッドの削除と必要なフィールドやメソッドの追加を行なっていくのが無難だと思います。

AbstractUserdjango.contrib.auth.models で定義されています。もし django.contrib.auth.models の具体的なパスが分からない場合は、下記ページで紹介しているような手順でクラスの定義を表示してみると良いと思います。

VSCodeでのクラス定義の確認方法の解説ページアイキャッチ【Django】VSCodeでクラスの定義を簡単に確認する方法

ちなみに、Django 4.0.5 の場合は AbstractUser の定義は下記のようなものになります(モデル名の変更と abstract = True のコメントアウトを行なっています)。

使用している Django のバージョンによっては、もしかしたらエラーが出る可能性がありますが、エラーが出ないのであれば下記をコピペしてモデルを定義するので良いと思います。

CustomUserの初期定義
from django.db import models
from django.contrib.auth.base_user import AbstractBaseUser
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.core.mail import send_mail
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import PermissionsMixin, UserManager

class CustomUser(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.

    Username and password are required. Other fields are optional.
    """

    username_validator = UnicodeUsernameValidator()

    username = models.CharField(
        _("username"),
        max_length=150,
        unique=True,
        help_text=_(
            "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
        ),
        validators=[username_validator],
        error_messages={
            "unique": _("A user with that username already exists."),
        },
    )
    first_name = models.CharField(_("first name"), max_length=150, 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,
        help_text=_("Designates whether the user can log into this admin site."),
    )
    is_active = models.BooleanField(
        _("active"),
        default=True,
        help_text=_(
            "Designates whether this user should be treated as active. "
            "Unselect this instead of deleting accounts."
        ),
    )
    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

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

    def get_full_name(self):
        """
        Return the first_name plus the last_name, with a space in between.
        """
        full_name = "%s %s" % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        """Return the short name for the user."""
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

AbstractUser をコピペする上で注意すべき点は、class Meta における abstract = True の行を削除(or コメントアウト)する必要があるところになります。

この行があると、AUTH_USER_MODEL にカスタムユーザーを設定した後にマイグレーション等を行うと下記のようなエラーが発生してしまうことになります。

django.core.exceptions.ImproperlyConfigured: AUTH_USER_MODEL refers to model 'login_app.CustomUser' that has not been installed

要は、abstract = True が設定されていると、抽象モデル(クラス)とみなされて実体が作成されません。なので、そのモデルを参照した際に「そんなモデルは存在しないよ」といったエラーが発生することになります。

ちなみに、先ほど触れた AUTH_USER_MODEL については下記ページで解説していますので、詳しく知りたい方は下記ページをご参照いただければと思います。

【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】

スポンサーリンク

カスタムユーザーのカスタマイズを行う

さて、先ほど定義した CustomUser(カスタムユーザー)は AbstractUser の定義をそのままコピペしてきただけなので、当然 AbstractUser と同等のモデルということになります。

さらに、下記ページでも解説しているように、UserAbstractUser と同等のモデルなので、上記で定義した CustomUserUser と同等のモデルということになります。

【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】

当然 User が持つフィールドを上記の CustomUser も持つことになるため、不要なフィールドがあれば削除し、さらに他に必要なフィールドがあれば追加してカスタマイズすることが必要になります。

カスタマイズできること

ここからは、そのカスタマイズの仕方について解説していきますが、まずは AbstractBaseUserPermissionsMixin を継承してカスタムユーザーを作成する場合に、上記で定義したカスタムユーザーに対してどのようなカスタマイズを行うことができるのかについて説明しておきます。

フィールド・メソッドの追加

まず、AbstractBaseUserPermissionsMixin を継承してカスタムユーザーを作成する場合、AbstractUser を継承してカスタムユーザーを作成するときと同様に「フィールド・メソッドの追加」を行うことが可能です。

カスタマイズしてフィールドを追加する様子

フィールド・メソッドの削除 / 変更

また、カスタムユーザーを定義する で定義したカスタムユーザーから「フィールド・メソッドの削除 / 変更」を行うこともできます。

AbstractUser を継承してカスタムユーザーを作成する場合でも、やろうと思えばフィールドの削除 / 変更を行うこともできるのですが、AbstractBaseUserPermissionsMixin を継承してカスタムユーザーを作成する方が削除 / 変更はしやすいと思います。

カスタマイズしてフィールドを削除する様子

ただし、特にフィールドを削除する際には注意が必要です。なぜなら、前述の通り、そのフィールドを他のクラスが利用している可能性があるからです。

そのクラスの具体的な例が、上記の CustomUser モデルの中でも使用している UserManager となります。UserManager には createsuperuser コマンド実行時に使用されるメソッドが定義されており、そのメソッドの中で CustomUser モデルで定義されるフィールドとして usernameemailis_staff が利用されています。

つまり、これらのフィールドが存在することを前提として動作しているクラスということになります。

ですので、上記で挙げた3つのフィールドを CustomUser から削除してしまうと、createsuperuser コマンド実行時に UserManager から例外が発生してしまって上手くスーパーユーザーの作成を行うことが出来ません。

このように、他のクラスから利用されているフィールドを削除する場合、単に削除しただけだとアプリ動作時に他のクラスで例外が発生する可能性があります。

この例外の発生を防ぐためには、その削除するフィールドを「利用しているクラス」の変更も必要になります。

削除するフィールドによっては他のクラスの変更も必要であることを示す図

各種設定

また、カスタムユーザーに対して各種設定を行うことも可能です。分かりやすい例が、ユーザーを一意に識別するフィールドの指定です。

カスタムユーザーにおいては、各ユーザーを一意に識別するためのフィールドが必要になります。デフォルトでは、そのユーザーを一意に識別するためのフィールドとして username が指定されています。

そのため、ユーザー作成時に入力された  username が重複している場合、エラーが発生してユーザー作成に失敗することになります。皆さんもユーザー登録時に「そのユーザー名は既に使用されています」などのエラーが出てユーザー作成に失敗した経験があるのではないかと思います。Django のウェブアプリにおいても、このような制御が行われています。

前述の通り、ユーザーを一意に識別するフィールドはデフォルトで username となっていますが、これを変更するようなことが可能です。例えば email に変更してやれば、各ユーザーは email で識別されるようになり、これによって username の重複を許したり、username フィールドを削除するようなことも可能になります。

カスタマイズによってユーザーを識別するフィールドを変更する様子

また、メールアドレスが設定されるフィールドを指定したり、createsuperuser コマンド時に入力受付を行うフィールドを設定したりすることも可能です。

こういった「各種設定」をカスタムユーザー作成時にカスタマイズすることが可能です。

まとめると、カスタムユーザーを定義する で定義したカスタムユーザーに対して下記のようなカスタマイズを行うことが出来ます。

  • フィールド・メソッドの追加
  • フィールド・メソッドの削除 / 変更
  • 各種設定

必要に応じて上記のようなカスタマイズを行えば良いのですが、フィールドの削除に関しては注意が必要で、削除するフィールドを利用しているクラスが存在する場合、そのクラスの変更も必要となります。

そのため、「カスタマイズを行うために必要な実装」としては大きく分けて下記の4つが挙げられると思います。

  • フィールド・メソッドの追加
  • フィールド・メソッドの削除 / 変更
  • 関連クラスの変更(削除するフィールドに応じて)
  • 各種設定

「フィールド・メソッドの追加」に関しては、やり方や注意点は AbstractUser を継承してカスタムユーザーを作成する時と同様ですので、このページでの解説は省略させていただきます。追加に関して詳しく知りたい方は、下記ページをご参照いただければと思います。

【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】

ここからは、上記における「フィールド・メソッドの削除 / 変更」「関連するクラスの変更」「各種設定」の具体的な手順について解説していきます。

具体的には、カスタムユーザーから emailis_staff 以外のフィールドを削除する実装例を示しながら、各手順について解説していきたいと思います(AbstractBaseUserPermissionsMixin から継承されるフィールドに関しては削除や変更は行いません)。

フィールド・メソッドの削除 / 変更

では、カスタムユーザーのカスタマイズにおける「フィールド・メソッドの削除 / 変更」について解説していきます。

前述の通り、ここではカスタムユーザーから emailis_staff 以外のフィールドを削除していきたいと思いますので、下記の5つのフィールドを削除することになります。

  • username
  • first_name
  • last_name
  • is_active
  • date_joined

単純にフィールドを削除するだけであれば、カスタムユーザーを定義する で定義した CustomUser(カスタムユーザー)を下記のように変更すれば良いだけになります。

フィールドの削除例
class CustomUser(AbstractBaseUser, PermissionsMixin):

    email = models.EmailField(_("email address"), blank=True)
    is_staff = models.BooleanField(
        _("staff status"),
        default=False,
        help_text=_("Designates whether the user can log into this admin site."),
    )

    objects = UserManager()

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

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

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

    def get_full_name(self):
        """
        Return the first_name plus the last_name, with a space in between.
        """
        full_name = "%s %s" % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        """Return the short name for the user."""
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

上記のように変更することで、emailis_staff 以外のフィールドが CustomUser から削除されることになります。

ただし、上記の CustomUser では、削除した first_name フィールドと last_name フィールドが get_full_name メソッドと get_short_name メソッドで使用されているため、これらのメソッドが実行されると例外が発生することになります。

ですので、get_full_name メソッドと get_short_name メソッドを削除する or 削除したフィールドを利用しないように変更する必要があります。

おそらくですが、これらのメソッドは他のクラスから利用されていないため、今回は単にメソッドの削除を行いたいと思います。

上記の2つのメソッドを削除した場合、CustomUser は下記のようになります。

メソッドの削除例
class CustomUser(AbstractBaseUser, PermissionsMixin):

    email = models.EmailField(_("email address"), blank=True)
    is_staff = models.BooleanField(
        _("staff status"),
        default=False,
        help_text=_("Designates whether the user can log into this admin site."),
    )

    objects = UserManager()

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

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

    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 an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

スポンサーリンク

依存クラスの変更(必要に応じて)

とりあえず上記の変更によって不要なフィールドを削除することができたことになります。

具体的に削除したフィールドは下記の5つになります。

  • username
  • first_name
  • last_name
  • is_active
  • date_joined

ただし、フィールドは削除できたものの、実は現状の CustomUser を利用するとアプリは正常に動作することができません。

なぜなら、先ほど削除したフィールドを利用するクラスが存在するからです(以降、フィールドを利用しているクラスを「依存クラス」と呼ばせていただきます)。

そのため、こういった依存クラスが動作する際に、削除されて存在しなくなったフィールドを利用しようとして例外が発生することになります。

そういった存在しなくなったフィールドが利用されないよう、依存クラスの変更が必要になります。

依存クラスを見つけ出す

この変更を行う際には、まず削除するフィールドに対する依存クラスを見つけてやる必要があります。フィールド名などで Django フレームワーク内の .py ファイルに対して文字列検索を行えば、この依存クラスを見つけることができると思います。

詳しい解説は省略させていただきますが、今回の場合、上記の5つのフィールドのうち、username 以外のフィールドに関しては管理画面(admin 画面)の変更を行わない限りは他のクラスの変更なしでもアプリとしては上手く動作してくれるはずです。

その一方で、username フィールドに関しては依存クラスが存在するため、そのクラスを変更しないとアプリとして上手く動作してくれません。

具体的には、現状だとスーパーユーザーを作成する際(createsuperuser コマンドを実行する際)に例外が発生してスーパーユーザーを作成することができません。

この原因はスーパーユーザー作成時に UserManager クラスが username フィールドを利用するようになっているからです。

そのため、次は UserManager を変更して username フィールドを利用しないようにすることで、スーパーユーザー作成を正常に実行できるようにしていきたいと思います。

依存クラスを変更する

依存クラスを変更する際には、削除するフィールドを依存クラスで利用している箇所を変更し、削除するフィールドを利用しないようにしてやる必要があります。

ただし、今回変更するクラスは前述のとおり UserManager になりますが、このクラスは Django フレームワーク内で定義されており、UserManager クラスを直接変更すると他のアプリにまで影響を及ぼしてしまうことになります。

そのため、別途 models.pyUserManager の定義をコピペし、その後 UserManager を変更していきたいと思います。これにより、UserManager の変更による影響をアプリ内(プロジェクト内)にとどめるようにします。

ということで、まずは models.pyUserManager の定義をコピペしましょう!

Django 4.0.5 の場合は、先ほどフィールドの削除を行なった CustomUser の定義も含めると、models.py は下記のようになります。import の追加や import の削除を行なっているので注意してください。

UserManagerの定義追加
from django.db import models
from django.apps import apps
from django.contrib import auth
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.core.mail import send_mail
from django.contrib.auth.hashers import make_password
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import PermissionsMixin

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, username, email, password, **extra_fields):
        """
        Create and save a user with the given username, email, and password.
        """
        if not username:
            raise ValueError("The given username must be set")
        email = self.normalize_email(email)
        # Lookup the real model class from the global app registry so this
        # manager method can be used in migrations. This is fine because
        # managers are by definition working on the real model.
        GlobalUserModel = apps.get_model(
            self.model._meta.app_label, self.model._meta.object_name
        )
        username = GlobalUserModel.normalize_username(username)
        user = self.model(username=username, email=email, **extra_fields)
        user.password = make_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, username, email=None, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", False)
        extra_fields.setdefault("is_superuser", False)
        return self._create_user(username, email, password, **extra_fields)

    def create_superuser(self, username, email=None, password=None, **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("Superuser must have is_staff=True.")
        if extra_fields.get("is_superuser") is not True:
            raise ValueError("Superuser must have is_superuser=True.")

        return self._create_user(username, email, password, **extra_fields)

    def with_perm(
        self, perm, is_active=True, include_superusers=True, backend=None, obj=None
    ):
        if backend is None:
            backends = auth._get_backends(return_tuples=True)
            if len(backends) == 1:
                backend, _ = backends[0]
            else:
                raise ValueError(
                    "You have multiple authentication backends configured and "
                    "therefore must provide the `backend` argument."
                )
        elif not isinstance(backend, str):
            raise TypeError(
                "backend must be a dotted import path string (got %r)." % backend
            )
        else:
            backend = auth.load_backend(backend)
        if hasattr(backend, "with_perm"):
            return backend.with_perm(
                perm,
                is_active=is_active,
                include_superusers=include_superusers,
                obj=obj,
            )
        return self.none()

class CustomUser(AbstractBaseUser, PermissionsMixin):

    email = models.EmailField(_("email address"), blank=True)
    is_staff = models.BooleanField(
        _("staff status"),
        default=False,
        help_text=_("Designates whether the user can log into this admin site."),
    )

    objects = UserManager()

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

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

    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 an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

まずは UserManager クラスに注目してみましょう!

各メソッドの定義を見てみると、引数や処理の中で username を使用していることが確認できると思います。

特に _create_user メソッドにおける下記の処理では、username を設定してデータベースのテーブルへのレコード保存が行われています。

usernameを使用している処理
user = self.model(username=username, email=email, **extra_fields)
user.password = make_password(password)
user.save(using=self._db)

ですが、カスタムユーザーから username フィールドを削除してしまうと、テーブルから username の列が削除されることになります(Django ではモデルに対してテーブルが作成され、そのテーブルの列はモデルの持つフィールド毎に用意される)。

そのテーブルに対して username の列にユーザー名が設定されたレコードを保存しようとしても、保存先のテーブルに username の列がないため、レコード保存時に例外が発生してしまいます。

CustomUserのテーブルにusernameの列が存在しないことを示す図

こういった例外が発生しないよう、カスタムユーザーから username フィールドを削除するためには、まずはこれらのメソッドの変更を行う必要があります。

詳細な解説は省略しますが、username フィールドを削除するためには、下記のように UserManager クラスを変更してやれば良いと思います。基本的には、単に username に関連する記述を削除しているだけです(引数を削除したり処理を削除したり)。

UserManagerのusername依存の除去
from django.db import models
from django.contrib import auth
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.core.mail import send_mail
from django.contrib.auth.hashers import make_password
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import PermissionsMixin

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        """
        Create and save a user with the given email and password.
        """
        if not email:
            raise ValueError("The given email must be set")
        email = self.normalize_email(email)
        # Lookup the real model class from the global app registry so this
        # manager method can be used in migrations. This is fine because
        # managers are by definition working on the real model.
        user = self.model(email=email, **extra_fields)
        user.password = make_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email=None, 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, email=None, password=None, **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("Superuser must have is_staff=True.")
        if extra_fields.get("is_superuser") is not True:
            raise ValueError("Superuser must have is_superuser=True.")

        return self._create_user(email, password, **extra_fields)

    def with_perm(
        self, perm, is_active=True, include_superusers=True, backend=None, obj=None
    ):
        if backend is None:
            backends = auth._get_backends(return_tuples=True)
            if len(backends) == 1:
                backend, _ = backends[0]
            else:
                raise ValueError(
                    "You have multiple authentication backends configured and "
                    "therefore must provide the `backend` argument."
                )
        elif not isinstance(backend, str):
            raise TypeError(
                "backend must be a dotted import path string (got %r)." % backend
            )
        else:
            backend = auth.load_backend(backend)
        if hasattr(backend, "with_perm"):
            return backend.with_perm(
                perm,
                is_active=is_active,
                include_superusers=include_superusers,
                obj=obj,
            )
        return self.none()

class CustomUser(AbstractBaseUser, PermissionsMixin):

    email = models.EmailField(_("email address"), blank=True)
    is_staff = models.BooleanField(
        _("staff status"),
        default=False,
        help_text=_("Designates whether the user can log into this admin site."),
    )

    objects = UserManager()

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

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

    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 an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

上記のように変更を行えば、UserManager クラスは username フィールドを使用しなくなるため、username フィールドを削除しても UserManager クラスは正常に動作することができるようになります。

前述でも簡単に触れましたが、UserManager はスーパーユーザー作成時に動作するクラスであり、上記の変更によって正常にスーパーユーザーを作成することができるようになります。

各種設定の変更

以上で、CustomUser からの不要なフィールドの削除と、その削除したフィールドを利用するクラスの変更が完了したことになります。

変更可能な設定

あとは、CustomUser の下記のクラス変数を変更することで、開発するアプリに合わせて CustomUser の各種設定の変更を行うことが出来ます。

  • EMAIL_FIELD:メールアドレスとして扱うフィールドの指定
  • USERNAME_FIELD:ユーザーを一意に識別するフィールドの指定
  • REQUIRED_FIELDScreatesuperuser コマンド実行時に入力受付を行うフィールドの指定

ちょっと分かりにくいのは USERNAME_FIELD だと思います。

USERNAME_FIELD はユーザー名として扱うフィールドではなく、ユーザーを一意に識別するためのフィールドを指定します。ユーザーを一意に識別するためのフィールドですので、USERNAME_FIELD で指定されたフィールドは、ユーザー毎に異なる必要があります。

また、上記の各クラス変数では指定の仕方にルールがあるので注意してください。

まず EMAIL_FIELD に関していえば、このクラス変数に指定されるフィールドはメールの送信先として利用されるため、当然メールアドレスとして扱うことのできるフィールドを設定する必要があります。

また、USERNAME_FIELD は前述の通りユーザーを一意に識別するためのフィールドですので、このクラス変数に指定されるフィールドの値は各ユーザーで重複しないようにする必要があります。

そのため、USERNAME_FIELD に指定するフィールドにおいては、unique オプションを True に設定しておく必要があります。

さらに、REQUIRED_FIELDS に指定するリストには、USERNAME_FIELD に指定するフィールドを含ませてはいけません。この辺りは下記ページで解説していますので、詳細については下記ページを参照していただければと思います。

【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】

各種設定の変更例

上記で挙げたクラス変数は、現状の CustomUser ではコピペ元の AbstractUser と同じように次のように設定が行われています。

デフォルト設定
EMAIL_FIELD = "email"
USERNAME_FIELD = "username"
REQUIRED_FIELDS = ["email"]

これらは開発するアプリや作成するカスタムユーザーに合わせて必要に応じて変更してやれば良いです。

ですが、今回に関しては username フィールドを削除しているため、上記の設定のままだと USERNAME_FIELD に指定しているフィールドがカスタムユーザーに存在しないのでエラーになってしまいます。ですので、今回の場合は必ず USERNAME_FIELD に username 以外を指定するよう変更してやる必要があります。

例えば、USERNAME_FIELDemail を指定してやれば、カスタムユーザーに存在するフィールドを USERNAME_FIELD に指定することになるのでエラーを解消することが出来ます。そして、この場合、ユーザーを一意に識別するフィールドとして email が使用されることになります。

ただし、USERNAME_FIELDemail を指定しただけだと、実はアプリ動作時やマイグレーション時にエラーが発生することになります。

これは、変更可能な設定 で挙げたルールに反しているからになります。

まず、現状の email フィールドには unique オプションに True が設定されていないため、USERNAME_FIELDemail を指定すると下記のようなエラーが発生することになります。

ERRORS:
login_app.CustomUser: (auth.E003) 'CustomUser.email' must be unique because it is named as the 'USERNAME_FIELD'.

また、REQUIRED_FIELDS に指定するリストに USERNAME_FIELD に指定した email が含まれるため、上記の設定のままだと次のようなエラーが発生することになります。

ERRORS:
login_app.CustomUser: (auth.E002) The field named as the 'USERNAME_FIELD' for a custom user model must not be included in 'REQUIRED_FIELDS'.
        HINT: The 'USERNAME_FIELD' is currently set to 'email', you should remove 'email' from the 'REQUIRED_FIELDS'.

こういったエラーを解消しつつ、USERNAME_FIELDemail を指定するためには、CustomUser を下記のように変更する必要があります。

各種設定の変更例
class CustomUser(AbstractBaseUser, PermissionsMixin):

    email = models.EmailField(_("email address"), unique=True)
    is_staff = models.BooleanField(
        _("staff status"),
        default=False,
        help_text=_("Designates whether the user can log into this admin site."),
    )

    objects = UserManager()

    EMAIL_FIELD = "email"
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []

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

    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 an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

USERNAME_FIELD と REQUIRED_FIELD の変更及び、最初の行の EmailField への引数指定で blank=True を削除し、代わりに unique=True を指定するようにしています(blank=True を削除することで入力必須フィールドとなる)。

前述の通り、これらのクラス変数の設定は開発するアプリや作成するカスタムユーザーに合わせて必要に応じて変更をすれば良いのですが、削除するフィールドによって設定が必須となる場合もあるので注意してください。

メールアドレス認証を行うサンプル

以上が、AbstractBaseUserPermissionsMixin を継承してカスタムユーザーを作成する際の基本的な流れとなります。

ここまでカスタムユーザーを作成する部分のみに焦点を当てて説明を行ってきましたので、最後にカスタムユーザーを作成する部分以外の手順も含めて「メールアドレス認証」を行うアプリの実装例を紹介していきたいと思います。

カスタムユーザーを作成する部分以外の手順は下記ページで紹介したものと同じですので、必要に応じて下記ページも参照しながら実装を進めていただければと思います。

【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】

スポンサーリンク

プロジェクトとアプリの作成

まずは、いつも通りプロジェクトとアプリを作成していきます。

最初に、下記コマンドを実行してプロジェクト login_project を作成します。

% django-admin startproject login_project

続いて、上記コマンドで作成される login_project フォルダの中に移動し、さらに startapp を実行してアプリ login_app を作成します。

% cd login_project
% python manage.py startapp login_app

次に、login_project フォルダの中にある settings.py 内の INSTALLED_APPS を下記のように変更し、プロジェクトに対してアプリを登録します。

アプリの登録
# Application definition

INSTALLED_APPS = [
    'login_app', # 追加
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

カスタムユーザーの作成

続いてカスタムユーザーを作成していきたいと思います。

作成するカスタムユーザーとしては、カスタムユーザーをカスタマイズする で示したカスタムユーザーと同じもの使用したいと思います。

このカスタムユーザーを使用すれば、USERNAME_FIELDemail が指定されているため email でユーザーを一意に識別することになり、さらに後述の手順を踏むことで email フィールドで認証が行われるようになるため、結果的にユーザー名ではなく、メールアドレスでの認証が行われるようになります。

ということで、login_app フォルダの中にある models.py を開き、下記のように変更してください。

カスタムユーザーの作成
from django.db import models
from django.contrib import auth
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.core.mail import send_mail
from django.contrib.auth.hashers import make_password
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import PermissionsMixin

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        """
        Create and save a user with the given email and password.
        """
        if not email:
            raise ValueError("The given email must be set")
        email = self.normalize_email(email)
        # Lookup the real model class from the global app registry so this
        # manager method can be used in migrations. This is fine because
        # managers are by definition working on the real model.
        user = self.model(email=email, **extra_fields)
        user.password = make_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email=None, 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, email=None, password=None, **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("Superuser must have is_staff=True.")
        if extra_fields.get("is_superuser") is not True:
            raise ValueError("Superuser must have is_superuser=True.")

        return self._create_user(email, password, **extra_fields)

    def with_perm(
        self, perm, is_active=True, include_superusers=True, backend=None, obj=None
    ):
        if backend is None:
            backends = auth._get_backends(return_tuples=True)
            if len(backends) == 1:
                backend, _ = backends[0]
            else:
                raise ValueError(
                    "You have multiple authentication backends configured and "
                    "therefore must provide the `backend` argument."
                )
        elif not isinstance(backend, str):
            raise TypeError(
                "backend must be a dotted import path string (got %r)." % backend
            )
        else:
            backend = auth.load_backend(backend)
        if hasattr(backend, "with_perm"):
            return backend.with_perm(
                perm,
                is_active=is_active,
                include_superusers=include_superusers,
                obj=obj,
            )
        return self.none()

class CustomUser(AbstractBaseUser, PermissionsMixin):

    email = models.EmailField(_("email address"), unique=True)
    is_staff = models.BooleanField(
        _("staff status"),
        default=False,
        help_text=_("Designates whether the user can log into this admin site."),
    )

    objects = UserManager()

    EMAIL_FIELD = "email"
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []

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

    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 an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

カスタムユーザーをカスタマイズする でも解説したように、username を削除する場合は createsuperuser コマンド実行時に上手くスーパーユーザーが作成できるよう、UserManager の変更も必要になります。

AUTH_USER_MODEL の設定

カスタムユーザーが作成できましたので、ここからはほぼ下記ページで示した手順でアプリを開発していくことになります。

【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】

まずは、settings.py の最後に下記を追記し、アプリ内で認証時に利用するユーザー管理モデルとして先ほど作成した CustomUserを設定します。

AUTH_USER_MODELの設定
AUTH_USER_MODEL = 'login_app.CustomUser'

スポンサーリンク

マイグレーションの実行

続いてマイグレーションを実行しましょう!

startproject で作成された login_project フォルダ内で下記の2つのコマンドを実行してみてください。おそらくエラーが発生することなく正常にマイグレーションが実行できるはずです。

% python manage.py makemigrations
% python manage.py migrate

このマイグレーションによってデータベースに login_app.customuser というテーブルが作成されることになります。

ポイントは、この login_app.customuser テーブルには、CustomUser から削除した username 等の列が存在しない点になります。

login_app.customuserのテーブル

スーパーユーザーの追加

次は管理画面でカスタムユーザーを管理できるように設定していきたいと思います。

この管理画面にアクセスするためにはスーパーユーザーが必要ですので、まずはスーパーユーザーを追加していきましょう!

このスーパーユーザーの追加を行うために、startproject で作成された login_project フォルダ内で下記コマンドを実行してみてください。

% python manage.py createsuperuser

上記コマンドを実行し、作成するスーパーユーザーのメールアドレスとパスワード(確認用も含めて2回)を入力すれば、スーパーユーザーの作成に成功するはずです。

% python manage.py createsuperuser
Email address: hanako@yamada.jp
Password: 
Password (again): 
Superuser created successfully.

いつも通りであれば、createsuperuser コマンド実行時にはユーザー名の入力受付も行われるはずですが、CustomUserUSERNAME_FIELDusername とは異なるフィールドに設定したため& REQUIRED_FIELDS に指定するリストに username を含ませていないため、ユーザー名の入力受付が行われないようになっています。

また、createsuperuser コマンド実行時に動作する UserManagerusername を使用しないように変更を行なったため、username の入力受付が行われなくても正常にスーパーユーザーの作成を行うことが可能となっています。

ちなみに、UserManager の変更を行わなかった場合、createsuperuser コマンド実行時には下記のようにエラーが発生することになります。

self.UserModel._default_manager.db_manager(database).create_superuser(
TypeError: create_superuser() missing 1 required positional argument: 'username'

管理画面の設定

次は、管理画面でカスタムユーザーを管理できるように設定を行なっていきます。

カスタムユーザーを管理画面から管理できるようにするためには、admin.pyadmin.site.register を実行してカスタムユーザーを登録することが必要になります。

ということで、login_app フォルダの下にある admin.py を下記のように変更しましょう!

カスタムユーザーの登録
from django.contrib import admin
from django.contrib.auth import get_user_model

CustomUser = get_user_model()

admin.site.register(CustomUser)

変更後に、まず下記コマンドを実行して Django の開発用ウェブサーバーを起動してください。

% python manage.py runserver

コマンド実行後、下記 URL をウェブブラウザで開いてみてください。

http://localhost:8000/admin/

すると、下の図のような管理画面へのログインフォームが表示されると思います。

ここでも入力受付が行われるのはメールアドレスとパスワードであり、管理画面への認証をメールアドレスによって行えるようになったことが確認できると思います。

このフォームでは、先ほど createsuperuser コマンドによって作成したスーパーユーザーのメールアドレスとパスワードを入力して Log in ボタンを押してください。

管理画面へのログインフォームが表示される様子

ログインに成功すれば、下の図のような画面が表示されるはずです。

表示される管理画面

この画面における LOGIN_APP セクション内の Users というリンクをクリックすれば、下の図のように「ユーザー一覧リスト」が表示されます。

ユーザー一覧リストが表示される様子

このユーザー一覧リストからユーザー名をクリックすれば、下の図のように「ユーザー詳細フォーム」が表示され、さらにここでユーザーの情報の変更も可能です。

ユーザー詳細情報が表示される様子

また、ユーザー一覧リストの右上にある ADD USER + のボタンをクリックすれば、下の図のような「ユーザ作成フォーム」が表示され、必要な入力を行なって画面下の SAVE ボタンをクリックすることでユーザーの追加を行うことができます(太字部分が入力必須項目となります)。

ユーザー追加フォームが表示される様子

これらのユーザー詳細フォームやユーザー作成フォームでは、CustomUser で削除したフィールド(usernamefirst_namelast_name など)の入力や変更が不可となっており、作成した CustomUser に応じて管理画面も変化していることを確認できると思います。

こんな感じで、admin.py の変更により、独自で作成したユーザーの管理を行うことができるようになります。

ただ、上記の「ユーザー一覧リスト」にはユーザー名しか表示されていませんので一覧性が悪いですし、「ユーザー詳細フォーム」や「ユーザー作成フォーム」では CustomUser の持つフィールド全てが表示されているので、ちょっと冗長な気もします。

さらに、ユーザー追加時等に入力したパスワードがそのまま表示されてセキュリティ的にもイマイチです。

パスワードがそのまま表示されてしまう様子

こういった具合に、ユーザー管理を行う画面・フォーム等に不満がある場合は管理画面のカスタマイズを行なった方が良いです。

この管理画面(ユーザー管理画面)のカスタマイズ方法については下記ページで解説していますので、必要に応じて参考にしていただければと思います。

Djangoでのユーザー管理画面のカスタマイズ方法の解説ページアイキャッチ【Django】ユーザー管理画面(admin)のカスタマイズ

スポンサーリンク

フォーム・ビュー・テンプレートの作成

ここまでの変更によって、カスタムユーザーが定義され、カスタムユーザーを管理画面から管理・作成することができるようになりました。

最後に、カスタムユーザーをフォームやビュー等から利用する例を紹介していきます。

実現するページは3つのみで、1つ目はユーザーアカウント(カスタムユーザー)を追加するページ、2つ目はログインを行うページ、3つ目はログインユーザーのメールアドレスを表示するページになります。

ユーザーアカウントの作成やログインに関しては下記ページで詳しく解説していますので、これらを実現するための手段の詳細を知りたい方は下記ページを参照して頂ければと思います。

Djangoでのログイン機能の実現方法解説ページアイキャッチ【Django】ログイン機能の実現方法(関数ベースビュー編)

このページでは、スクリプトの紹介および、ユーザーに関連する説明のみを行なっていきたいと思います。

フォームの作成

まずは login_app フォルダの下に forms.py を新規作成し、中身を下記のように変更します。

forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import get_user_model

CustomUser = get_user_model()

class SignupForm(UserCreationForm):
    class Meta:
        model = CustomUser
        fields = ['email']

class LoginForm(AuthenticationForm):
    class Meta:
        model = CustomUser
        fields = ['email', 'password']

UserCreationFormAuthenticationForm に関しては先ほど紹介した  【Django】ログイン機能の実現方法(関数ベースビュー編) で解説していますので、詳細を知りたい方は【Django】ログイン機能の実現方法(関数ベースビュー編) をご参照ください。

各フォームの model には models.py に定義した CustomUser を指定する必要があります。この CustomUser は、settings.pyAUTH_USER_MODELmodels.py で定義した CustomUser を指定している場合、上記のように get_user_model から取得することが可能です。

また、SignupFormfields にはユーザー作成時(アカウント登録時)にユーザーから入力受付を行うフィールドを指定する必要があります。今回はメールアドレスとパスワードの入力受付を行うため、上記のように指定を行なっています(SignupFormfields への指定においては、パスワードの指定は省略可能です)。

さらに、LoginFormfields にはログイン時にユーザーから入力受付を行うフィールドを指定する必要があります。今回はメールアドレス認証を行うため、メールアドレスとパスワードの入力受付のみを行うよう、上記のように指定を行なっています。

これらの fields の指定においては、model に指定したモデルに存在しないフィールドを指定するとエラーになるので注意してください。今回の場合、CustomUser から username を削除しているわけですから、username を指定するとエラーになることになります。

こんな感じで、モデルの持つフィールドに合わせる形で、フォームやビュー、テンプレートを作成していく必要があるので注意してください。

テンプレートの作成

続いてテンプレートを作成していきます。

今回用意するテンプレートは基本的に下記ページの ログイン機能実現用のスクリプト で紹介している signup.htmllogin.htmluser.html とほぼ同等のものなので、詳細な説明は省略させていただきます(user.html はメールアドレスのみを表示するように変更しています)。

Djangoでのログイン機能の実現方法解説ページアイキャッチ【Django】ログイン機能の実現方法(関数ベースビュー編)

これらのファイルの作成フォルダが login_app/templates/login_app/ であることに注意してください。

signup.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ユーザー登録</title>
</head>
<body>
    <h1>ユーザー登録</h1>
    <form action="{% url 'signup' %}" method="post">
    {% csrf_token %}
        {{ form.as_p }}
        <p><input type="submit" value="登録"></p>
    </form>
</body>
</html>
login.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ログイン</title>
</head>
<body>
    <h1>ログイン</h1>
    <p>{{message}}</p>
    <form action="{% url 'login'%}" method="post">
    {% csrf_token %}
        {{ form.as_p }}
        <p><input type="hidden" name="next" value="{{next}}"></p>
        <p><input type="submit" value="ログイン"></p>
    </form>
    <p><a href="{% url 'signup'%}">ユーザー登録</a></p>
</body>
</html>
user.html
{% load static %}
<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>あなたの情報</title>
</head>
<body>
    <h1>{{email}}</h1>
</body>

</html>

ビューの作成

次はビューを作成していきます。

こちらも基本的に下記ページの ログイン機能実現用のスクリプト で紹介している views.py と似ている部分が多いのですが、簡略化して logout_viewother_view を削除したり、リダイレクト関連の処理を省略している部分もあるので注意してください。

Djangoでのログイン機能の実現方法解説ページアイキャッチ【Django】ログイン機能の実現方法(関数ベースビュー編)

また、user_view においては、引数 request のデータ属性 user にはログイン中のユーザーに対する CustomUser のインスタンスが設定されています。なので、ログインユーザーのメールアドレスは request.user.email から取得することが可能です。

views.py
from django.shortcuts import render, redirect
from .forms import SignupForm, LoginForm
from django.contrib.auth import login
from django.contrib.auth.decorators import login_required

def signup_view(request):
    if request.method == 'POST':

        form = SignupForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect(to='/login_app/user/')

    else:
        form = SignupForm()
    
    param = {
        'form': form
    }

    return render(request, 'login_app/signup.html', param)

def login_view(request):
    if request.method == 'POST':
        next = request.POST.get('next')
        form = LoginForm(request, data=request.POST)

        if form.is_valid():
            user = form.get_user()

            if user:
                login(request, user)
                return redirect(to='/login_app/user/')

    else:
        form = LoginForm()

    param = {
        'form': form,
    }

    return render(request, 'login_app/login.html', param)

@login_required
def user_view(request):
    user = request.user

    params = {
        'email': user.email
    }

    return render(request, 'login_app/user.html', params)

パスとビューの関連付け

最後にパスとビューの関連付けを行なっていきます。

まず login_project フォルダの下の urls.py を下記のように変更し、login_app/ から始まるパスが指定された際に login_app フォルダ下の urls.py を参照するようにします。

login_project/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login_app/', include('login_app.urls'))
]

続いて、その参照先となる urls.py を login_app フォルダの下に下記のように作成します。

これにより、path の第1引数で指定したパス(URL)へのアクセスがあった際に、第2引数で指定した views.py の各関数が実行されるようになります。

login_app/urls.py
from . import views
from django.urls import path

urlpatterns = [
    path('signup/', views.signup_view, name='signup'),
    path('login/', views.login_view, name='login'),
    path('user/', views.user_view, name='user'),
]

動作確認

ここまでフォームやビューの作成を行なってきましたので動作確認をしてみましょう!

まずは Django 開発用ウェブサーバーを起動し(python manage.py runserver を実行)、さらに下記 URL をウェブブラウザで開いてみてください。

http://localhost:8000/login_app/signup/

そうすると、下の図のようなユーザー登録フォームが表示されるはずです。

表示されるユーザー登録画面

forms.py で SignupFormfields に指定したフィールド&パスワードの入力を行うことができるようになっている点を確認できると思います。

さらに、上の図の登録フォームにメールアドレスとパスワードの入力を行なった後に 登録 ボタンを押せば、次は下の図のようなユーザーの情報表示ページに遷移するはずです。

表示されるユーザーの情報

今回の場合は表示されるのがメールアドレスのみとなりますが、CustomUser にフィールドを追加しておけば、そのフィールドの情報をログイン中のユーザーに合わせて表示するようなことも可能です。例えばユーザーのアバターを表示することなども可能です。

最後に下記 URL をウェブブラウザで表示してみてください。

http://localhost:8000/login_app/login/

今度は下の図のようなログインフォームが表示されるはずです。

表示されるログイン画面

このログインフォームに、スーパーユーザーの追加 で追加したユーザーもしくは先程のユーザー登録フォームで作成したユーザーのメールアドレスとパスワードを入力して ログイン ボタンを押してみてください。ログインに成功し、先ほど同様にユーザー情報表示ページに遷移するはずです。

いつもはユーザー名とパスワードによって認証を行なっていたのが、メールアドレスとパスワードによって認証されるようになっており、目的のメールアドレス認証を実現できていることが確認できると思います!

スポンサーリンク

まとめ

このページでは、Django での「カスタムユーザー」の作り方について解説しました!

カスタムユーザーは AbstractBaseUser、さらには PermissionsMixin を継承することで作成することが出来ます。

AbstractUser よりも作成できるカスタムユーザーとしては自由度は高いですが、その分カスタムユーザー作成時の難易度は高いと思います。

そのため、まずは カスタムユーザーを定義する で解説したように AbstractUser 同等のモデルを定義し、そのモデルを カスタムユーザーをカスタマイズする で解説したようにカスタマイズする手順を踏むのが無難だと思います。

もちろん慣れてきたら、AbstractBaseUser と PermissionsMixin を継承するモデルを定義し、そのモデルをゼロから作り上げていくのでも良いと思います。

特にウェブアプリにとってはユーザー管理が重要ですので、こういったカスタムユーザーの作り方などはしっかりマスターしておくと良いと思います!

同じカテゴリのページ一覧を表示

コメントを残す

メールアドレスが公開されることはありません。