14
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Django】ユーザーモデルAbstractBaseUserの実装(Twitterっぽく)

Last updated at Posted at 2021-09-17

ユーザーモデルの種類

  • User(標準モデル)
  • AbstractUser
  • AbstractBaseUser(オススメ)

各モデルの違い

標準モデル(難易度低)

 簡単だが応用が効かない。

models.py
from django.contrib.auth.models import User

AbstractUser(難易度中)

 フィールドのカスタマイズ(追加・変更)ができる。

models.py
from django.contrib.auth.models import AbstractUser

AbstractBaseUser(難易度高)

 フィールドのカスタマイズ(追加・変更・削除)ができる。

models.py
from django.contrib.auth.models import AbstractBaseUser

公式推奨

 後でユーザーモデルを変更するのは、超大変なので最初からAbstractBaseUserを使うのがオススメだそうです。

チュートリアル等はUser(標準モデル)でも十分です。

初回migrate前にAbstractBaseUserを定義する

プロジェクト開始前にどのタイプにするかよく考える事

デフォルトのフィールド

デフォルトのフィールドを参考にする。

引数はあまり気にせず、どんなフィールドが作られるのかだけを見てもらえればOKです!

この中からよく使われそうな物をピックアップして、さらに新たなフィールドを追加してみたいと思います。

ちなみに、User(標準モデル)AbstractUserを使った場合は、以下のフィールドが作成されます。

migtarion.py
fields=[
    ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
    ('password', models.CharField(max_length=128, verbose_name='password')),
    #最終ログイン日時
    ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
    #スーパーユーザー真偽
    ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
    #ユーザーネーム
    ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
    #ファーストネーム
    ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
    #ラストネーム
    ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
    #メールアドレス
    ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
    #スタッフ権限
    ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
    #ログイン可不可
    ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
    #登録した日時
    ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
    #グループ
    ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
    #パーミッション
    ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
],

モデルの作成

とりあえず適当に必要なフィールドを書いちゃいます。

  • ユーザーネーム(Twitterの@以降を想定)
  • email
  • ニックネーム
  • 誕生日
  • プロフィール画像
  • 血液型(choices=のサンプルとして)
  • url
  • 自己紹介文
  • 登録日
  • ログインの可否(管理者用)
models.py
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin

#usernameでバリーデートするために入れときました(無くてもOK)
from django.core.validators import MinLengthValidator, RegexValidator

class MyUser(AbstractBaseUser, PermissionsMixin):
    BLOOD_CHOICES = [
        ("A型","A型"),
        ("B型","B型"),
        ("AB型","AB型"),
        ("O型","O型")
        ]

    username = models.CharField(verbose_name='username', max_length=10, unique=True, validators=[MinLengthValidator(5,), RegexValidator(r'^[a-zA-Z0-9]*$',)])
    email = models.EmailField(verbose_name='Email', max_length=50, unique=True)
    nickname = models.CharField(verbose_name='ニックネーム', max_length=10, blank=False, null=False)
    date_of_birth = models.DateField(verbose_name="誕生日", blank=True, null=True)
    image = models.ImageField(verbose_name='プロフィール画像', upload_to="image/", blank=True, null=True)
    blood_type = models.TextField(verbose_name="血液型", choices=BLOOD_CHOICES, blank=True, null=True)
    url = models.URLField(verbose_name='リンク', blank=True, null=True)
    introduction = models.TextField(verbose_name='自己紹介', max_length=300, blank=True, null=True)
    date_joined = models.DateTimeField(verbose_name='登録日', auto_now_add=True)
    is_active = models.BooleanField(default=True)

    is_staff = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False)

    #AbstractBaseUserにはMyUserManagerが必要
    objects = MyUserManager()
    #一意の識別子として使用されます
    USERNAME_FIELD = 'email'
    #ユーザーを作成するときにプロンプ​​トに表示されるフィールド名のリストです。
    REQUIRED_FIELDS = ['username']

    def __str__(self):
        return self.username

is_activeについて

イメージしやすいように例えるとBAN機能のようなものです。
チェックを外すとそのユーザーがログインできなくなります。

MyUserManager

管理者が管理コマンド上(ターミナル等)でユーザーを作成する時に必要です。次の項目で書きます。

USERNAME_FIELD

ログインする時の一意の識別子です。
Twitterであれば、emailとかusernameになりますね!

unique=Trueのフィールドのみ設定できます。

REQUIRED_FIELDS

管理者が管理コマンド上(ターミナル等)でユーザーを作成する時に表示されるリストです。

上記では'username'だけになっていますが、複数設定することも可能です。

models.py
    #複数設定する場合はこんな感じ
    REQUIRED_FIELDS = ['username', 'date_of_birth']

MyUserManagerの実装

前述の通り、管理者が管理コマンド上(ターミナル等)でユーザーを作成する時に必要なので作成したMyUser(AbstractBaseUser)の上に書いておく。

models.py
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser, PermissionsMixin
from django.core.validators import MinLengthValidator, RegexValidator

class MyUserManager(BaseUserManager):
    def create_user(self, username, email, password=None):
        if not username:
            raise ValueError('Users must have an username')
        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(
            username=username,
            email=self.normalize_email(email),
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, email, password):
        user = self.create_user(
            username=username,
            email=self.normalize_email(email),
            password=password,
        )
        user.is_admin=True
        user.is_staff=True
        user.is_superuser=True
        user.save(using=self._db)
        return user

class MyUser(AbstractBaseUser, PermissionsMixin):
    #省略

大体こんな感じで大丈夫!

変更するとしたらif not hoge:と同時にuser=の引数くらいでしょうか?

他はコピペで大丈夫だと思います。

デフォルトのユーザーモデルに上書き

上書きしないとエラーになるのでsetting.pyに設定を書きます。

setting.py
AUTH_USER_MODEL = 'アプリ名.モデルのクラス名'

#サンプル
AUTH_USER_MODEL = 'accounts.MyUser'

マイグレーションしてみる

terminal
$ python manage.py makemigrations

$ python manage.py migrate

スーパーユーザーの作成

スーパーユーザーはmigrate後に作る事

terminal
$ python manage.py createsuperuser

まとめ

AbstractBaseUserはかなり応用が効くみたいで、SNS認証が必要な場合にも使えます。

サービス公開を目的とする場合は是非AbstractBaseUserを利用してみて下さい!

応用が効きすぎて色んな設定がありますので、詳しく知りたい方は公式ドキュメントを参照してくださいね!

14
16
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?