Help us understand the problem. What is going on with this article?

Django ユーザー カスタマイズ方法

More than 1 year has passed since last update.

この記事について

Django の ユーザーのカスタマイズ方法についてまとめました。
公式サイトではこのあたりの内容です。
https://docs.djangoproject.com/ja/2.0/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project

こちらのエントリで指摘している通り、Djangoでは基本的にカスタマイズしたUserを使うべきと思います。
参考:Djangoでは常にカスタムUserを使用すべき

バージョン
Django 2.0.1

カスタマイズ方法

基本的に以下の作業手順ですすめます。

1.usersという名前でアプリケーションを作成
2.users.models にAbstractBaseUserを継承したUserクラスを作成
3.users.admin に UserAdmin を継承したクラスを作成。
4.設定ファイルに「AUTH_USER_MODEL = 'users.User'」を定義
5.django.contrib.admin、django.contrib.authへの影響範囲を調べ、必要であれば他のクラスも継承して独自に修正。

アプリケーションを独立させると、他プロジェクトでの再利用性と、アプリケーション単位でダンプファイル操作が可能になるメリットがあります。

「users」はcookiecutter-djangoでも使われている名前です。こちらで統一するのがよいと思います。
参考:cookiecutter-djangoを使ってみた

AbstractUser か AbstractBaseUser か

ユーザーモデルのカスタマイズ方法にはAbstractUserを継承する方法とAbstractBaseUserを継承する方法があります。

AbstractUserは抽象クラスAbstractBaseUserの実装です。
AbstractUserもとにカスタマイズすると柔軟性が低いがコーディング量は少なく、AbstractBaseUserを使うと柔軟性が高いがコーディング量は多くなるという関係です。

カスタマイズの内容が属性の追加変更のみのときはAbstractUser、属性の削除またはAbstractUserのカスタマイズで収まらないときはAbstractBaseUserを使うことが推奨されています。

しかし、私見ですが、どのような状況でもAbstractBaseUserを利用したほうがいいと思います。その理由は以下の3点です。

・AbstractBaseUserの方が柔軟性がある
・コードが分散しないので変更時に修正場所がわかりやすい
・デフォルトの属性(first_name、last_name、email)を使わない場合すぐ消せる

「AbstractUser vs AbstractBaseUser」というキーワードで検索すると色々考察がでてきます。自分に適した方法を採ってください。

https://stackoverflow.com/questions/21514354/difference-between-abstractuser-and-abstractbaseuser-in-django

サンプルコード

練習として、需要ありそうな要件でカスタマイズをしてみます。

・idをUUIDに変更
・first_nameとlast_nameは削除し、full_nameを追加
・所属(department)を追加。兼任できるようにNxN関係とする。

ログインIDをメールアドレスにする変更はこちらのページで紹介されています。
naritoブログ Djangoで、Userモデルのカスタマイズ(継承)

project/setting.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
]

# Application definition
AUTH_USER_MODEL = 'users.User'

users/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, UserManager
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
import uuid as uuid_lib

# Create your models here.


class Department(models.Model):
    """所属 兼任可"""

    name = models.CharField(_('所属'), max_length=150, blank=True)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = _('所属')
        verbose_name_plural = _('所属')


class User(AbstractBaseUser, PermissionsMixin):
    """ユーザー AbstractUserをコピペし編集"""

    uuid = models.UUIDField(default=uuid_lib.uuid4,
                            primary_key=True, editable=False)
    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."),
        },
    )
    full_name = models.CharField(_('氏名'), max_length=150, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    departments = models.ManyToManyField(
        Department,
        verbose_name=_('所属'),
        blank=True,
        help_text=_('Specific Departments for this user.'),
        related_name="user_set",
        related_query_name="user",
    )

    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')

    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.full_name

    def get_short_name(self):
        return self.full_name

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

# Register your models here.
@admin.register(Department)
class AdminDepartment(admin.ModelAdmin):
    pass

@admin.register(User)
class AdminUserAdmin(UserAdmin):

    fieldsets = (
        (None, {'fields': ('username', 'password')}),
        (_('Personal info'), {'fields': ('full_name', 'email','departments')}),
        (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser',
                                       'groups', 'user_permissions')}),
        (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
    )
    list_display = ('username', 'email', 'full_name', 'is_staff')
    search_fields = ('username', 'full_name', 'email')
    filter_horizontal = ('groups', 'user_permissions','departments')


この通りカスタマイズできました。

image.png

参考

admin.ModelAdmin(UserAdminのスーパークラス)のカスタマイズについてはこちらのエントリが参考になります。

Django 管理画面逆引きメモ

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした