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

# 本当にやるべき？
[[Django公式]プロジェクトの開始時にカスタムのユーザーモデルを使用する](https://docs.djangoproject.com/ja/2.1/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project "[Django公式]プロジェクトの開始時にカスタムのユーザーモデルを使用する")
> 新しくプロジェクトを始める場合は、デフォルトの User で十分である場合でも、カスタムユーザーモデルを作成することを強く推奨します。

らしいです。

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

# 今回作ったもの
以下に置いてます。
[【github】gaku3601/django_customuser](https://github.com/gaku3601/django_customuser)

# 早速やってみる
PJ、ならびにaccountアプリケーションを作成する。

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

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

```py: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モデル(後で作成)をデフォルトで使用する認証ユーザモデルと設定するため、下記も追記しておく。

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

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

```py: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も以下のように編集する。

```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を作成、適用する。

```bash:コマンド
$ python manage.py makemigrations
$ python manage.py migrate
```

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

```bash:コマンド
$ python manage.py createsuperuser
$ python manage.py runserver
```

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

![スクリーンショット 2019-02-21 11.13.56.png](https://qiita-image-store.s3.amazonaws.com/0/47132/1b17085d-f3fb-63cb-001e-0dbd748d25ae.png)

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

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

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

# 参考文献
[Django ユーザー カスタマイズ方法](https://qiita.com/okoppe8/items/10ae61808dc3056f9c8e)