本当にやるべき?
[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の設定に追加したアプリケーションを追記する。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'account', #追記
]
また、作成したアプリケーションのUserモデル(後で作成)をデフォルトで使用する認証ユーザモデルと設定するため、下記も追記しておく。
AUTH_USER_MODEL = 'account.User'
ユーザモデルを作成していきます。これは、django/django【models.py】のAbstractUser classとUserManager classをほぼコピーして利用しているだけのものです。今回はカスタムユーザの下地作りが目的なので、項目追加等の編集は行いません。
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も以下のように編集する。
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画面にアクセスし確認します。
AUTHENTICATION AND AUTHORIZATIONにデフォルトで存在するUsersがなくなり、新たに作成したACCOUNTにUsersが作成されました🎉
おわりに
昨日Pythonを導入したド素人がDjangoでjwtトークン認証を実装できるか試してみた
で、デフォルトの「AUTHENTICATION AND AUTHORIZATION」のUsersをデフォルトのUsersモデルに利用することは、「編集ができない」という面で大変気持ち悪いと記載しました。
しかし、カスタムユーザを作成し利用することで、自由にユーザモデルの定義を行えるので、この気持ち悪さを払拭することができました。
ただ、django.contrib.auth.base_user.AbstractBaseUserを継承してUserモデルを作るのは、結構なハードルとなりますので、もうちょっと楽になると嬉しいなと感じました。(誰かプルリクお願いします。