🚀

DjangoのCustomUserの設定

2024/03/25に公開

アプリケーションの立ち上げ

まずは、以下のコマンドでアプリケーションを作成します。
今回はCustomUserの設定をしていきます。

docker compose run backend python manage.py startapp accounts

CustomUserモデルの作成

コード全体は以下のように設定しました。
CustomUserの設定は様々だと思いますし、開発するアプリケーションによって欲しい設定は異なると思います。あくまでこの記事ではこのようにしたということで書いていきます。

accounts/models.py

backend/accounts/models.py
from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.core.mail import send_mail
from django.db import models
from django.utils import timezone
from datetime import date
from django.utils.translation import gettext_lazy as _

import uuid

class UserManager(BaseUserManager):
  use_in_migrations = True
  
  def _create_user(self, email, username, password = None, **extra_fields):
    if not email:
      raise ValueError(_('The Email field must be set.'))
    email = self.normalize_email(email)
    user = self.model(email = email, username = username, **extra_fields)
    user.set_password(password)
    user.save(using = self._db)
    return user
    
  def create_user(self, email, username, password = None, **extra_fields):
    extra_fields.setdefault("is_staff", True)
    extra_fields.setdefault("is_superuser", False)
    return self._create_user(email, username, password, **extra_fields)
  
  def create_superuser(self, email, username, password = None, **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 habe is_staff = True.'))
    if extra_fields.get('is_superuser') is not True:
      raise ValueError(_('Superuser must have is_superuser = True.'))
    
    return self._create_user(
      email,
      username,
      password,
      **extra_fields
    )
  
class CustomUser(AbstractBaseUser, PermissionsMixin):
  username_validator = UnicodeUsernameValidator()
  
  uuid = models.CharField(default = uuid.uuid4, primary_key = True, editable = False)
  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."),
    },
  )
  email = models.EmailField(_('email address'), unique=True, blank = False)
  first_name = models.CharField(_('first name'), max_length=30, blank=True)
  last_name = models.CharField(_('last name'), max_length=30, blank=True)
  birth_date = models.DateField(blank = True, null = True)
  is_active = models.BooleanField(
    _('active'),
    default=True,
    help_text=_(
        "Designates whether this user should be treated as active. "
        "Unselect this instead of deleting accounts."
    ),
  )
  is_staff = models.BooleanField(
    _('staff status'),
    default=False,
    help_text=_("Designates whether the user can log into this admin site."),
  )
  date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
  
  objects = UserManager()
  
  EMAIL_FIELD = "email"
  USERNAME_FIELD = "email"
  REQUIRED_FIELDS = ["username", 'birth_date']
  
  class Meta:
    verbose_name = _('User')
    verbose_name_plural = _('users')
  
  def clean(self):
    super().clean()
    self.email = self.__class__.objects.normalize_email(self.email)

  def get_full_name(self):
    full_name = "%s %s" % (self.first_name, self.last_name)
    return full_name.strip()

  def get_short_name(self):
    return self.first_name

  def email_user(self, subject, message, from_email=None, **kwargs):
    send_mail(subject, message, from_email, [self.email], **kwargs)
  
  def get_age(self):
    if self.birth_date:
      today = date.today()
      return today.year - self.birth_date.year -((today.month, today.day) < (self.birth_date.month, self.birth_date.day))
    return None

  def __str__(self):
    return self.username

accounts/forms.py

backend/accounts/forms.py
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser

class MyUserChangeForm(UserChangeForm):
  class Meta:
    model = CustomUser
    fields = '__all__'

class MyUserCreationForm(UserCreationForm):
  class Meta:
    model = CustomUser
    fields = ('email',)

accounts/admin.py

backend/accounts/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.utils.translation import gettext_lazy as _
from .models import CustomUser
from .forms import MyUserChangeForm, MyUserCreationForm

class MyUserAdmin(UserAdmin):
  fieldsets = (
    (None, {"fields": ("email", "password")}),
    (_("Personal info"), {"fields": ("username", "first_name", "last_name", "birth_date")}),
    (_("Permissions"), {
      "fields": (
        "is_active",
        "is_staff",
        "is_superuser",
        "groups",
        "user_permissions"
      )
    }),
    (_("Important dates"), {"fields": ("last_login", "date_joined")}),
  )
  
  add_fieldsets = (
    (None, {
      "classes": ("wide",),
      "fields": ("email", "password1", "password2"),
    }),
  )
  
  form = MyUserChangeForm
  add_form = MyUserCreationForm
  
  list_display = ("email", "username", "is_staff")
  list_filter = ("is_staff", "is_superuser", "is_active", "groups")
  search_fields = ("email", "username")
  ordering = ("email",)
  
admin.site.register(CustomUser, MyUserAdmin)

マイグレーション

以下のコマンドでマイグレーションを実行します。

docker compose run backend python manage.py makemigrations
docker compose run backedn python manage.py migrate

正常に実行されると思います。

superuserの設定

以下のコマンドにより、superuserを作成します。

docker compose rn backend python manage.py createsuperuser

管理画面

管理画面にアクセスします。

docker compose up

指定されたURLにアクセスして、/adminにアクセスするとログイン画面が開きます。
また、先ほど作成したsuperuserを使用してログインすると、管理画面が表示されると思います。

以上がCustomUserの設定です。

Discussion