【Django】Userモデルのカスタマイズ

#はじめに

業務とは別に個人的に何かサービスを作りたいなと思ったので色々勉強しているのですが、
その中でUserモデルをカスタマイズしたいなと思った時があったのでそれについて記載します。

Djnago歴は浅くまだまだ学習中の身ですので、何かありましたらご指摘お願いします！

なお、本記事は[コチラ](https://qiita.com/okoppe8/items/10ae61808dc3056f9c8e)を参考に作成しました。

##実装
既存のプロジェクトをいじっていく予定ですので、最初の設定などは飛ばしています。

また、今回はYouTuber向けのサービスのアカウント情報を登録する程で作ってみました。
例えば、first_nameやlast_nameを削除しchannel_nameを入れたりといった感じ。


手順としては下記の手順で進めていきます。
1. usersアプリ作成
2. AbstractBaseUserを継承したUserクラスを作成
3. UserAdmin を継承したクラスを作成。

まず`users`アプリを作成します。

```
python manage.py startapp users
```

アプリを作成したら`settings.py`に追加する必要があるので追記します。

それと一緒に`AUTH_USER_MODEL`も追記します。
`'users.User'`のusersは先程作成したアプリ名です。

```python:settigs.py
INSTALLED_APPS = [
    'mainApp',
    'users',    #### 追加
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

AUTH_USER_MODEL = 'users.User'
```

次に、`users/models.py`にUserモデルを記載します。
とりあえずコードは下記の通りになります。

コードの説明は下に記述しております。

```python:users/models.py
from django.contrib.auth import validators
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.core.mail import send_mail
from django.utils import timezone
import uuid as uuid_lib

class User(AbstractBaseUser, PermissionsMixin):
    uuid = models.UUIDField(
        default=uuid_lib.uuid4,
        primary_key=True,
        editable=False
    )

    username_validators = UnicodeUsernameValidator()

    username = models.CharField(
        "ユーザ名",
        max_length=150,
        unique=True,
        help_text="※150文字以下の文字や数字、一部の記号で入力したください。",
        validators = [username_validators],
        error_messages={
            "unique": "このユーザー名は既に使用されています。",
        },
    )

    channel_name = models.CharField("チャンネル名", max_length=150, blank=True)
    email = models.EmailField("Eメールアドレス", blank=True)
    
    is_staff = models.BooleanField(
        "ユーザステータス",
        help_text="ユーザーがこの管理サイトにログインできるかどうかを指定します。",
        default=False,
    )

    is_member = models.BooleanField(
        "会員ステータス", 
        help_text="このユーザが契約しているかを区別します。",
        default=False,
    )

    is_active = models.BooleanField(
        "アクティブユーザ",
        help_text="このユーザーをアクティブとして扱うかどうかを指定します。アカウントを削除する代わりに、これを選択解除してください。",
        default=True,
    )

    date_joined = models.DateTimeField("登録日", default=timezone.now)

    objects = UserManager()

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

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

    def get_full_name(self):
        return self.username

    def get_short_name(self):
        return self.username
```

`AbstractBaseUser`
Userモデルのカスタマイズを行うために必要なクラス

`PermissionsMixin`
AbstractBaseUserはパーミッション関連の機能を持っていないので、
パーミッションの機能を利用したい場合は、PermissionsMixinを同時に継承しておく。

`UserManager`
UserManagerを使うということをDjangoに知らせています。 

これにより、今後「create_user」、「create_superuser」のメソッドを呼ぶときに
UserManagerクラスの「create_user」、「create_superuser」のメソッドが呼ばれる。

`UnicodeUsernameValidator`
不正な文字列が含まれていないかチェックするためのクラスらしいです。

`send_mail`
簡単なメールに使うためのメソッド

`uuid4`
![uuid.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/022f7a68-9cd2-00d4-35fc-0f62806cb9f4.png)

`primary_key`
そのカラムが　プライマリーキーになり、 id は生成されなくなります。

`editable`
編集ができなくなる

`unique`
被りNG

`help_text`
Formでこういう風に入力してほしいとヘルプ（ヒント）をつけたいときに記載

`validators`
先程定義したバリデーションを記載

`verbose_name`
管理画面のモデル名を指定

`verbose_name_plural`
複数形の場合の名称を指定

といった感じです。
次にadmin.pyを修正していきたいと思います。

```python:users/admin.py
from django.contrib.auth.admin import UserAdmin
from django.contrib import admin

from .models import User


@admin.register(User)
class AdminUserAdmin(UserAdmin):
    fieldsets = (
        (None, {"fields": ("username", "password")}),
        ("Personal info", {"fields": ("channel_name", "email")}),
        ("Permissions", {"fields": ("is_active", "is_member", "is_staff", "is_superuser", "groups", "user_permissions")}),
        ("Important dates", {"fields": ("last_login", "date_joined")}),
    )

    list_display = ("username", "channel_name", "is_member", "last_login")
    search_fields = ("username", "channel_name", "email")
    filter_horizontal = ("groups", "user_permissions")

```

fieldsetsで管理画面に表示する内容を指定します。
タプル型で記載をしていきます。

第一引数のタプルですが、Noneを指定することにより下画像の
ユーザ名、パスワードの箇所の設定を行います。
![スクリーンショット 2021-07-26 23.20.15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/6ed49394-60b9-e13a-0420-2a787488353a.jpeg)

つまり、`(None, {"fields": ("username", "password")})`の箇所を
`(None, {"fields": ("username", "password", "channel_name)})`とすれば、
管理画面では、ユーザ名、パスワード、チャンネル名が表示されます。

そしてその下に、`Personal info`が表示されます。
Personal infoの内容もfieldsetsで指定可能です。

models.pyとadmin.pyの記述が完了したら、
`makemigrations`と`migrate`、`createsuperuser`を実行します。

```
$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py createsuperuser
```

なお、マイグレーションなどが失敗する場合はmigrationsディレクトリを削除したり、
データベースごと削除してしまうと解決が早いっぽいです。

私はデータベースを削除しました・・・。

#さいごに
以上でUserモデルのカスタマイズができました！

最初のマイグレーションの時にカスタマイズを行っていないとダメという制約がありますが、
最初にカスタマイズの設定をすれば後で変更することもできると思うので、
とりあえずカスタマイズする感じになるんですかね？

なかなか大変でしたがなんとか書き切れました・・・。

最後までご覧いただきありがとうございました！

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