共通化とカスタムユーザー作成

目次

Django共通化とカスタムユーザー作成

djangoでは、公式サイトで標準のUserモデルを使用せずにCustomUserを使うようにするように推奨されています。

これは、初回のマイグレするまでに行っていないといけませんのでご注意ください。

accountsアプリの作成

それでは、アプリを作成します。

python manage.py startapp accounts

これで、アプリが作成されました。

settings.pyに登録

アプリを作ったらsettingsファイルにアプリを登録する。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # アプリ
    'accounts',
]

modelsとviewsフォルダ作成

modelsとviewsは、肥大化しやすいのでフォルダを作成して、そこでファイルを目的ごとに分けて管理します。

フォルダの中に、必ず__init__.pyファイルを作成してDjnagoに認識させてください。

__init__.pyには、同じフォルダに作成したファイルを読み込みするように設定すると問題なく使用できます。

from .accounts import *

共通項目はUtilsで作成

modelsには、最終更新日や作成日などは、どのmodelでも必要なフィールドである場合、共通化して使うとコードの冗長化と漏れの防止が図れます。

manage.pyがある階層にutilsというフォルダを作成します。

その中に、base_model.pyファイルと__init__.pyファイルを作成します。

__init__.py には、base_model.pyファイルを読み込みするように追記します。

from .base_model import *

base_model.pyに共通項目を記載

ますはコードから。

from abc import ABCMeta, abstractmethod
from concurrency.fields import AutoIncVersionField
from django_boost.models.mixins import TimeStampModelMixin
from django.db import models
from django.conf import settings


class BaseModel(TimeStampModelMixin):
    version = AutoIncVersionField(verbose_name='バージョン')
    posted_at = models.DateTimeField(verbose_name='作成日時', auto_now_add=True)
    updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True)

    # モデルを単体では作成しない
    class Meta:
        abstract = True

versionは、楽観的排他制御を簡単に設定することができるdjango-concurrency 用のフィールドです。

インストールを実行します。

pip install django-concurrency

これで、AutoIncVersionFieldが、読込時と書き込み時に異なっている場合、エラーとなり保存がされないようになります。

論理削除が実装 django-boost

通常は、データベースから削除処理を行うとデータベースから物理的に削除され復旧ができない状況になります。最近では、データは物理削除でなく論理削除することが多くなっています。

必要な場合は論理削除で、消しても問題ないものは物理削除すればよいと思います。

まずは、django-boostをインストールします。

pip install django-boost

settings.pyにアプリを追加します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # サードパーティ
    'django_boost', # アンダーバーに注意
    # アプリ
    'accounts',
]

あとは、論理削除を使いたいモデルに、deleted_atを追加することで利用することができます。

公式サイト参照

from django.db import models
from django_boost.models.mixins import LogicalDeletionMixin

class Model(LogicalDeletionMixin):
    deleted_at = models.DateTimeField(
        verbose_name="削除日時", blank=True, null=True, default=None, editable=False
    )

モデルをLogialDeletionMixinを継承して、deleted_atフィールドを作成することで、利用することができます。

# 論理削除
data.deleted()

# 物理削除
data.delete(hard=True)

# 論理削除日時取得
data.deleted_at

# 論理削除されていない取得
data.alive()

# 論理削除されているものを取得
data.dead()

# 実際のqueryset
datas = Model.objects.alive()

使い方も簡単です。

自分でフィールドを作成して、クエリセットの条件を書くことで対応しても構わないと思います。

カスタムユーザーモデル作成

それでは、次にカスタムユーザーを作成します。

先ほど作成した、modelsの中にあるaccounts.pyにモデルの情報を記載していきます。

公式サイトでは、EmailをIDとして利用するモデルのサンプルが紹介されています。

今回作成するUserモデルは、こちらのサイトも参考に作成させていただいています。

from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.db import models
from django.contrib.auth.models import UserManager as DjangoUserManager
from django.contrib.auth.models import AbstractUser, AbstractBaseUser, PermissionsMixin
from django.contrib.auth.validators import UnicodeUsernameValidator
from datetime import timezone
from django.contrib import auth
from django.utils import timezone
import uuid
from utils.base_model import *

class UserManager(DjangoUserManager):
    use_in_migrations = True

    def _create_user(self, username, email, password, **extra_fields):
        """
        Create and save a user with the given username, email, and password.
        """
        if not username:
            raise ValueError("The given username must be set")
        email = self.normalize_email(email)
        username = self.model.normalize_username(username)
        user = self.model(username=username, email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, username, email=None, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", False)
        extra_fields.setdefault("is_superuser", False)
        return self._create_user(username, email, password, **extra_fields)

    def create_superuser(self, username, email=None, 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 have is_staff=True.")
        if extra_fields.get("is_superuser") is not True:
            raise ValueError("Superuser must have is_superuser=True.")

        return self._create_user(username, email, password, **extra_fields)

    def with_perm(
        self, perm, is_active=True, include_superusers=True, backend=None, obj=None
    ):
        if backend is None:
            backends = auth._get_backends(return_tuples=True)
            if len(backends) == 1:
                backend, _ = backends[0]
            else:
                raise ValueError(
                    "You have multiple authentication backends configured and "
                    "therefore must provide the `backend` argument."
                )
        elif not isinstance(backend, str):
            raise TypeError(
                "backend must be a dotted import path string (got %r)." % backend
            )
        else:
            backend = auth.load_backend(backend)
        if hasattr(backend, "with_perm"):
            return backend.with_perm(
                perm,
                is_active=is_active,
                include_superusers=include_superusers,
                obj=obj,
            )
        return self.none()


class User(AbstractBaseUser, PermissionsMixin, BaseModel):
    username_validator = UnicodeUsernameValidator()
    user_id = models.UUIDField(
        verbose_name="ユーザーID", primary_key=True, default=uuid.uuid4, editable=False
    )
    username = models.CharField(
        ("username"),
        max_length=150,
        unique=True,
        help_text=(
            "150文字以内で入力してください。使用できる記号は、 @/./+/-/_ です。"
        ),
        validators=[username_validator],
        error_messages={
            "unique": ("すでに登録されています。"),
        },
    )
    first_name = models.CharField(("名前"), max_length=30, blank=True)
    last_name = models.CharField(("苗字"), max_length=150, blank=True)
    email = models.EmailField(("Eメール"), blank=True)
    is_staff = models.BooleanField(
        ("管理者権限"),
        default=False,
        help_text=("Designates whether the user can log into this admin site."),
    )
    is_active = models.BooleanField(
        ("有効"),
        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 = []

    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.last_name, self.first_name)
        return full_name.strip()

    def get_short_name(self):
        return self.last_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        send_mail(subject, message, from_email, [self.email], **kwargs)

    def __str__(self):
        return "%s %s" % (self.first_name, self.last_name)

認証モデルを変更する

作成したカスタムユーザーを認証するモデルとして指定します。

Settingsファイルに追記します。

AUTH_USER_MODEL = "accounts.User"

これで、いよいよマイグレを行います。

python manage.py makemigrations
python manage.py migrate

これでUserモデルが作成されました。

スーパーユーザーを作成

ここまできたら、スーパーユーザーを作成します。

python manage.py createsuperuser

usernameとpasswordを入力するとスーパーユーザーが作成されます。

管理画面の表示を作成

Djangoの特徴といっていい、管理画面。

テーブルとadminの設定を少し書くだけで、各テーブルを編集できる管理画面が自動で作成されます。

これは、開発工数の削減につながり、スピード開発のひとつの手助けになっている機能です。

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models.accounts import *

class UserAdmin(UserAdmin):
    model = User
    ordering = ('username', 'first_name', 'is_active', 'is_staff')
    list_display = (
        'username',
        'get_full_name',
        'is_active',
        'is_staff',
    )
admin.site.register(User, UserAdmin)

これで、管理画面に表示されるようになります。

http://127.0.0.1:8000/admin/にアクセスしてみください。

これでカスタムユーザーの作成は終了です。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

「@縁紡ぐ」にご訪問頂きありがとうございます。
業務改善やIT化、システム開発・プロジェクトチーム運用支援を行っています。
お気軽にご相談ください。

コメント

コメントする

CAPTCHA


目次