Help us understand the problem. What is going on with this article?

Djangoではカスタムユーザを作るべきらしい

More than 1 year has passed since last update.

本当にやるべき?

[Django公式]プロジェクトの開始時にカスタムのユーザーモデルを使用する

新しくプロジェクトを始める場合は、デフォルトの User で十分である場合でも、カスタムユーザーモデルを作成することを強く推奨します。

らしいです。

django.contrib.auth.base_user.AbstractBaseUser
django.contrib.auth.models.AbstractUser
どちらかを継承してカスタムユーザを作るのですが、今回はより柔軟性があるけど難しいと言われる「AbstractBaseUser」を継承して作っていきます。

今回作ったもの

以下に置いてます。
【github】gaku3601/django_customuser

早速やってみる

PJ、ならびにaccountアプリケーションを作成する。

コマンド
$ django-admin startproject customUserPJ .
$ python manage.py startapp account

アプリケーションを追加したので、PJの設定に追加したアプリケーションを追記する。

customUserPJ/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'account', 追記
]

また、作成したアプリケーションのUserモデル(後で作成)をデフォルトで使用する認証ユーザモデルと設定するため、下記も追記しておく。

customUserPJ/settings.py
AUTH_USER_MODEL = 'account.User'

ユーザモデルを作成していきます。これは、django/django【models.py】のAbstractUser classとUserManager classをほぼコピーして利用しているだけのものです。今回はカスタムユーザの下地作りが目的なので、項目追加等の編集は行いません。

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

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)
        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=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, 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('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)

class User(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=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,
        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)

admin画面で編集できるようにadmin.pyも以下のように編集する。

account/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User

admin.site.register(User, UserAdmin)

作成したuserモデルに対して、migrationを作成、適用する。

コマンド
$ python manage.py makemigrations
$ python manage.py migrate

adminのsuperuserを作成、システムを起動する。

コマンド
$ python manage.py createsuperuser
$ python manage.py runserver

ブラウザでadmin画面にアクセスし確認します。

スクリーンショット 2019-02-21 11.13.56.png

AUTHENTICATION AND AUTHORIZATIONにデフォルトで存在するUsersがなくなり、新たに作成したACCOUNTにUsersが作成されました🎉

おわりに

昨日Pythonを導入したド素人がDjangoでjwtトークン認証を実装できるか試してみた
で、デフォルトの「AUTHENTICATION AND AUTHORIZATION」のUsersをデフォルトのUsersモデルに利用することは、「編集ができない」という面で大変気持ち悪いと記載しました。
しかし、カスタムユーザを作成し利用することで、自由にユーザモデルの定義を行えるので、この気持ち悪さを払拭することができました。

ただ、django.contrib.auth.base_user.AbstractBaseUserを継承してUserモデルを作るのは、結構なハードルとなりますので、もうちょっと楽になると嬉しいなと感じました。(誰かプルリクお願いします。

参考文献

Django ユーザー カスタマイズ方法

gaku3601
🎉筋肉系エンジニアになりたい系プログラマ٩( 'ω' )و🎉 好きなことで食っていく。(`・ω・´) twitterとかもやってますので、絡んでいただけると嬉しく思います♪
persol-pt
労働人口の減少が見込まれる現代において、 一人ひとりが生み出す付加価値を最大化することが、 日本における重要なテーマです。 わたしたちは、 人・プロセスデザイン・テクノロジーの力で、 お客さまのビジネスプロセスに変革をもたらし、 人と組織の生産性を高めていきます。 さらに、はたらくを楽しむことを大切にし、 「はたらいて、笑おう。」の世界を実現します。
https://www.persol-pt.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away