Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
185
Help us understand the problem. What is going on with this article?
@okoppe8

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」というキーワードで検索すると色々考察がでてきます。自分に適した方法を採ってください。

サンプルコード

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

・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 管理画面逆引きメモ

185
Help us understand the problem. What is going on with this article?
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
okoppe8
札幌でシステムエンジニアをしています。 「Djangoで業務システム作る時に必要な機能をあらかじめ用意する」というテーマでやってます。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
185
Help us understand the problem. What is going on with this article?