LoginSignup
6
3

More than 1 year has passed since last update.

Django Custom User Model の作成

Last updated at Posted at 2022-02-06

環境

Windows 11 Home
Python 3.10.2
Django 4.0.2
venv利用あり

背景

Djangoにおいてユーザモデルはカスタムユーザモデルを作成するのがセオリーらしいので記事にした。
本ページでは触れないが、後々、初回ログイン時にパスワードを強制変更させるためのカラムを用意しておく。

関連記事

Django 第1回:Django Custom User Model の作成 今回
Django 第2回:Django 初回ログイン時にパスワード変更を強制する
Django 第3回:Django 一定期間パスワードを変更していないユーザにパスワード変更を強制する
Django 第4回:Django ランダムかつ有効期限のあるURLを生成し、上位者に承認してもらいアカウントを発行する 
Django 第5回:Django パスワード試行回数ロックとランダムかつ有効期限付きURLでの本人確認による解除

状態

python startproject mysiteでプロジェクトを構築したばかりの状態。
カスタムユーザモデルを作成し、EmailとPasswordの認証に変更する。

手順:mysite/settings.py

以下コマンドでusersAppを作成する。
python manage.py startapp users

続いてsettings.pyに先ほど作成したusersを認識させるのとAUTH_USER_MODELを定義する。

mysite\settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # Local
    'users', #追加
]
AUTH_USER_MODEL = 'users.User' # 追加

...
LANGUAGE_CODE = 'ja' # 変更

TIME_ZONE = 'Asia/Tokyo' # 変更

##手順:user/models.py

users\models.py

from django.utils import timezone
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager
from django.core.mail import send_mail
import uuid as uuid_lib

class UserManager(UserManager):
    def _create_user(self, email, password, **extra_fields):
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user
    
    def create_user(self, email, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, 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.')

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

class User(AbstractBaseUser, PermissionsMixin):
    """Custom User"""
    class Meta:
        verbose_name = 'ユーザ'
        verbose_name_plural = 'ユーザ'

    uuid = models.UUIDField(default=uuid_lib.uuid4, primary_key=True, editable=False) # 管理ID
    username = models.CharField(max_length=30, unique=False) # ユーザ氏名
    email = models.EmailField(unique=True, blank=True, null=True) # メールアドレス = これで認証する

    is_active = models.BooleanField(default=True) # アクティブ権限
    is_staff = models.BooleanField(default=True) # スタッフ権限
    is_superuser = models.BooleanField(default=False) # 管理者権限
    date_joined = models.DateTimeField(default=timezone.now) # アカウント作成日時
    password_changed = models.BooleanField(default=False) # パスワードを変更したかどうかのフラグ
    password_changed_date = models.DateTimeField(blank=True, null=True) # 最終パスワード変更日時

    objects = UserManager()

    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'email'
    REQUIRED_FIELD = ''

    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_mail(subject, message, from_email, [self.email], **kwargs)

    def __str__(self):
        return self.email

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

verbose_nameは管理画面でUsersではなく日本語で表示させるためのもの。
email = models.EmailField(unique=True, blank=True, null=True)の中でblank=True, null=Trueがないと、管理画面で
ユーザを追加する際にIntegrityError: UNIQUE constraint failed: users_user.emailのエラーが出るので注意。

設定なし
image.png

設定あり
image.png

:user/admin.py
models.pyだけでは変更が確認できないので、管理画面にカスタムユーザを表示させる。

users\admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext, gettext_lazy as _
from .models import User

@admin.register(User)
class UserAdmin(UserAdmin):
    fieldsets = (
        (None, {'fields': ('username', 'password')}),
        (_('Personal Info',), {'fields': ('email',)}),
        (_('Permissions',), {'fields': ('is_active', 'is_staff', 'is_superuser',)}),
        (_('Password',), {'fields': ('password_changed', 'password_changed_date',)}),
        (_('Important Dates',), {'fields': ('last_login', 'date_joined',)}),
    )

    list_display = ('username', 'email', 'is_active',)

##手順:migrationとrunserver
Visual Studio Code insiders(以降、VSCode)からターミナルを開き、
python manage.py makemigrations でmakemigrationファイルを作成
image.png

python manage.py migrate でmigrateを実行
image.png

python manage.py createsuperuser で管理者権限のアカウントを作る
admin@admin.com と入力したが好きなメールアドレスを入力してOK
パスワードは2回入力するが、画面には表示されない
パスワードを簡単なものにしたため警告が出たが無視してかまわない。あとで変更できる。
image.png

実行。
python manage.py runserver
image.png

http://127.0.0.1:8000/ にアクセスするとロケットが飛んでいる。サーバ立ち上げ成功の印。
image.png

http://127.0.0.1:8000/admin で管理画面に遷移する。
先ほどのEmailとPasswordを入力してログインする。
image.png

ログイン成功
image.png

ユーザを押下
先ほどのアカウントが表示されている。
フィルタや検索ボックスがあるが、カスタマイズ可能。別の記事で紹介したい。
image.png

先ほどのアカウントをクリック
詳細が表示される。
python:users\admin.pyで設定しているが詳細は別の記事で紹介したい。
image.png

お疲れさまでした。

参考

Django 第1回:Django Custom User Model の作成
Django 第2回:Django 初回ログイン時にパスワード変更を強制する
Django 第3回:Django 一定期間パスワードを変更していないユーザにパスワード変更を強制する
Django 第4回:Django ランダムかつ有効期限のあるURLを生成し、上位者に承認してもらいアカウントを発行する 
Django 第5回:Django パスワード試行回数ロックとランダムかつ有効期限付きURLでの本人確認による解除

6
3
0

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
6
3