Django のログイン認証で使用するユーザモデルをカスタマイズする

Django のログイン認証で使用するユーザモデルをカスタマイズします。

最近知ったのですが、Django はユーザモデルをカスタマイズすることを推奨していました。

新しくプロジェクトを始める場合は、デフォルトの User で十分である場合でも、カスタムユーザーモデルを作成することを強く推奨します。

https://docs.djangoproject.com/ja/2.2/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project

ユーザモデルを拡張しやすくするためです。今後、新規に Django プロジェクトを作成するときは、なるべくカスタムユーザモデルでいこうと思います。

バージョン

  • Python 3.5.4
  • Django 2.2.1

インストール

ユーザモデルをカスタマイズするための環境を作っていきます。

仮想環境と Django のインストール

$ python -m venv venv
$ source venv/bin/activate
(venv) $ pip install Django

virtualenv で Python 仮想環境を作成し、そこに Django をインストールします。

Django プロジェクト作成

(venv) $ django-admin startproject custom_auth_user
(venv) $ cd custom_auth_user/
(venv) $ python manage.py startapp account

Django のプロジェクトとアプリケーションを作成します。プロジェクト名とアプリケーション名は好きにつけましょう。

実装

カスタムユーザモデルの実装に入る前に、Django ビルトインユーザモデルを確認したいと思います。

Django ビルトイン ユーザモデル

django.contrib.auth.models.User

フィールド 必須 説明
username o ユーザ名。認証で使用。
password o パスワード。認証で使用。
email メールアドレス。
first_name ファーストネーム。名前。
last_name ラストネーム。苗字。
groups グループ。
user_permissions 権限。
is_staff admin サイトにアクセスできるか?
is_active このユーザは有効か?
is_superuser スーパーユーザか?
last_login 最終ログイン日時。
date_joined ユーザ作成日時。

これらが Django ビルトインユーザモデルです。認証用のユーザモデルとして settings.py にデフォルト設定されています。このままでも十分な気もしますが、要件によっては、このままではダメなケースもあると思います。

私は、ログイン認証をメールアドレスにしないといけない案件がありましたので、カスタムユーザモデルを使って実装しました。

カスタムユーザモデル

models.py

from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
from django.db import models
from django.utils import timezone

class MyUserManager(BaseUserManager):
    def create_user(self, email, password):
        if not email:
            raise ValueError('Users must have an email address')
        
        user = self.model(email=self.normalize_email(email))
        user.set_password(password)
        user.save(using=self._db)
        return user
        
    def create_superuser(self, email, password):
        user = self.create_user(email=email, password=password)
        user.is_superuser = True
        user.save(using=self._db)
        return user

class MyUser(AbstractBaseUser):
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []
    
    email = models.EmailField(unique=True, max_length=255)
    username = models.CharField(max_length=150, blank=True)
    is_active = models.BooleanField(default=True)
    is_superuser = models.BooleanField(default=False)
    date_joined = models.DateTimeField(default=timezone.now)
    
    objects = MyUserManager()

カスタムユーザモデルのクラス名を MyUser としました。そして、認証に必要だった username を USERNAME_FIELD の設定で、email に変更しています。カスタムユーザモデルは、ユーザの作成関数も独自のものにする必要があるため、カスタムユーザマネージャが必要になります。MyUserManager という名前にしました。

settings.py

INSTALLED_APPS = [
    ...
    'account.apps.AccountConfig',
]

...
AUTH_USER_MODEL = 'account.MyUser'

カスタムユーザモデルを作成したら、それを settings.py で指定します。パスを指定するのですが、models は不要なことに注意してください。

動作確認

それでは、動作確認していきます。

まず最初に、DB のマイグレーションを実行し、manage.py createsuperuser コマンドでカスタムユーザが作成できるか確認します。

DB マイグレーション

(venv) $ python manage.py makemigrations
Migrations for 'account':
  account/migrations/0001_initial.py
    - Create model MyUser
(venv) $ python manage.py migrate
Operations to perform:
  Apply all migrations: account, admin, auth, contenttypes, sessions
Running migrations:
  Applying account.0001_initial... OK
  Applying contenttypes.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0001_initial... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying sessions.0001_initial... OK

makemigrations コマンドでカスタムユーザ用の migration ファイルを作成し、migrate コマンドでスキーマを適用します。

スーパーユーザの作成

(venv) $ python manage.py createsuperuser
Email: test@test.test
Password:
Password (again):
Superuser created successfully.

createsuperuser コマンドでユーザを作成します。ビルトインユーザモデルの場合、最初に username の入力を求められますが、email に変わっていることが確認できます。

DB の確認

$ sqlite3 db.sqlite3
sqlite> .tables
account_myuser auth_permission django_migrations
auth_group django_admin_log django_session
auth_group_permissions django_content_type
sqlite> .schema account_myuser
CREATE TABLE IF NOT EXISTS "account_myuser" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "password" varchar(128) NOT NULL, "last_login" datetime NULL, "email" varchar(255) NOT NULL UNIQUE, "username" varchar(150) NOT NULL, "is_active" bool NOT NULL, "is_superuser" bool NOT NULL, "date_joined" datetime NOT NULL);
sqlite> select * from account_myuser;
1|pbkdf2_sha256$150000$u4MnNZS8Ewzb$PgMPH42orRPxEiwMFqPfsyLKw0mZ1FiwyBqh8zWcViQ=||test@test.test||1|1|2019-05-16 03:57:08.789679

実際にユーザが作成されたか DB を確認します。select * from account_myuser; で先ほど作成したカスタムユーザモデルのユーザが作成されていることが確認できました。

まとめ

Django のログイン認証で使用するユーザモデルをカスタマイズしました。

Django はカスタムユーザモデルを推奨しているので、なるべくカスタムユーザモデルを使いましょう。

カスタムユーザモデルの作り方は、まず、ユーザモデルとユーザマネージャを作成し、settings.py で指定すれば OK です。