Django ユーザ情報のカスタム

 

どうしてユーザもモデルをカスタムで用意する?

どのようなものを作りたいのかにもよる問題ではありますが、

サービスを考えたらユーザ情報を扱いたいと考えることは多いのでは

ないでしょうか?

個人で使うものだったそのようなものは不要ですが、

ゆくゆくはサービスを提供するとなったらユーザ情報をもつことは

必要です。

 

Djangoには、最初のmigrationしたときにユーザ情報用のテーブルを

用意しています。(このテーブルを以後ユーザモデルといいます)

ですが、models.pyの中を探しても該当するテーブルの記述がないので

ユーザ情報をカスタマイズしたいといったときに行いにくくなっています。

最大の問題は、

パスワード認証に使うものがusername固定になっています。

認証したいときにメールアドレスを使いたいときなどは

カスタムすることが必須になります。

 

私の個人的な意見ですが、

最初からカスタムユーザモデルを用意したほうがいいと思います。

Djangoは初期にテーブルを用意されているときに

ユーザモデルのテーブルの外部キーを張ってるテーブルが存在しています。

ある程度作った後に用意したりするといろいろと不整合が起きたりして

大変だと思います。

ですので、内容を変更しなくてもカスタム定義はしておいたほうが

あとで幸せだと思います。

 

カスタマイズ方法

AbstractUserかAbstractBaseUserか?

Djangoのユーザ情報をカスタマイズするには、

AbstractUserかAbstractBaseUserを継承したクラスにする必要があります。

これらを継承する特徴としては、

・AbstractUserの場合

 既存のユーザモデルを使用してカスタムする方法

 既存のユーザモデルらカラムをなくす場合には、削除したりする宣言をする

 必要がある。

・AbstractBaseUserの場合

 1からモデルを作るようになります。

 必要なカラムもすべて宣言する必要があります。

 

言葉で説明するとこんな感じです・・・・

一見するとAbstractUserでいいんじゃないの?

って気がしますよね!

あるのにわざわざ1から作る必要ないじゃん!

って思う人も少なくないはず

判断の大事なところは

 今回用意するユーザモデルをどうしたいか?

です。

AbstractUserで用意すると簡単に用意できるが、

元のユーザモデルを把握していないと

不要なカラムを作ってしまったりしてしまします。

 

AbstractBaseUserなら足しながら進められるが、

最初にある程度の設計が必要になります。

 

検索するとどっちがいいかという議論もあるようです。

私個人の意見ですが、

個人でいろいろ勉強や開発をするためならAbstractUserでいいと思います。

サービスで提供するならAbstractBaseUserをお勧めします。

サービスで提供→明確のユーザモデルの設計があるという感覚でいます。

今回は両方のやり方を書いていきます。

参考にしてもらえれば幸いです。

 

今後の方針

今回は両方試しますが、私はAbstractBaseUserで今後を進めます。

私の選択理由ですが、なんとなく設計方針が固まっていること

そして、せっかく勉強するなら制約がない方針でやりたいからです。

制約がないと理解しないと作れないから勉強んいなるやん!

という発想です。

 

今あるユーザモデルの確認

作業を始める前に今のユーザモデルを確認します。

どのカラムが必要なのか考えるためにも今の現状把握は大事ですね!

 

何も変更しないで作られるユーザモデルのテーブルは

「auth_user」になります。

モデルの詳細は別途機会を持ちたいと思っていますが、

Djangoは自動でテーブル名称を複数系にすることはありません。

構成は以下のものになります。

カラム名 データ型 補足
id int PK
password varchar(128)  
last_login datetime(6)  
is_superuser tinyint(1)  
username varchar(150) indexあり
first_name varchar(30)  
last_name varchar(150)  
email varchar(254)  
is_staff tinyint(1)  
is_active tinyint(1)  
date_joined datetime(6)  

usernameはユーザの氏名ではなくログインIDになります。

usernameには一意制約が設けられていました。

 違いがわかりにくいのはis_superuserとis_staffの違いです。

Djangoはコマンドを実行するとスーパーユーザを作ることができます。

デフォルトで用意されていることもあり、

ユーザモデルをカスタムする際には影響があります。

想定している権限管理として、

 ・一般権限

 ・スタッフ権限

 ・スーパーユーザ権限

の3つを想定しているようです。

 

 

手順

AbstractUserでもAbstractBaseUserでもやることは同じです。

1.Djangoプロジェクトにアプリケーションを追加する

 (すでにv1というアプリを追加しているので今回は省略します)

2.Managerを用意

2.models.pyに追加します。

3.settings.pyに追加します

になります。

2番目の手順の追加内容が継承するモデルによって変わります。

Managerは、先ほどすこし触れましたが、ユーザはコマンドで作ることができます。

ユーザモデルをカスタムするとこちらも影響を受けるので

用意する用意する必要があります。

 

今回すること

今回は、認証にusernameの代わりにemailを使って認証できる設定とします。

usernameなのですが、氏名ではなくログインIDが入る予定のカラムです。

emailで認証できるようにするのでこのカラムはなくしたいと思います。

また、権限管理がユーザモデル内にあってほしくないので

is_staff、is_superuserをなくします。

date_joinedも作成したことしか残らないので、created_atとupdate_atにしようと思います。

 

AbstractUser編

Mangerを用意するためにv1の中にmanagers.pyを用意します。

その中には、

from django.contrib.auth.base_user import BaseUserManager


class CustomUserManager(BaseUserManager):
    def create_user(selfemailpassword, **extra_fields):
        if not email:
            raise ValueError(_('The Email must be set'))
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save()
        return user

    def create_superuser(selfemailpassword, **extra_fields):
        extra_fields.setdefault('is_active'True)

        return self.create_user(email, password, **extra_fields)

とします。

BaseUserMangerがDjangoで用意しているユーザモデルのManagerです。

 

 AbstractUserを継承するmodels.pyは以下のようになります。

from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils import timezone
from .managers import CustomUserManager

class CustomUser(AbstractUser):
    username = None
    is_superuser = None
    is_sutaff = None
    email = models.EmailField(unique=True)
    created_at = models.DateTimeField(default=timezone.now)
    updated_at = models.DateTimeField(auto_now=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = 

    objects = CustomUserManager()

    class Meta:
        db_table="user"
        
    # 氏名をフルネームにして返す
    def get_full_name(self):
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def __str__(self):
        return self.email

 氏名をフルネームで出力したいこともあるだろうと思い、

そのメソッドまで書いています。

 

最後にsettings.pyに以下の内容を追記します。

# Application definition
AUTH_USER_MODEL = 'v1.User'

デフォルトで記述されていないので

お好みの場所に書いてください。

 

ここまでできたら、

APIコンテナに入って、以下のコマンドを実行します。

python manage.py makemigrations
python manage.py migrate

 

作業前に一度マイグレーションしているとmakemigrationsはできると思いますが、

migrateでエラーになると思います。

そのときは、

v1/migrationsの中にある_init_.py以外を削除します。

DBのテーブルもすべて削除します。

この状態でmakemigrationsからやり直してください。

 

migrateまで実行できたらDBに確認しに行きましょう。

 models.pyで

    class Meta:
        db_table="user"

と書いているので作成するテーブル名を指定しているので

確認するテーブルは「user」になります。

試しにユーザを作ってみましょう。

APIコンテナ内で

python manage.py createsuperuser

 になります。

作ったユーザを確認すると

パスワードはハッシュ化されています。

created_atやupdate_atは日本時間ではなくUTC(標準時間)で入ります。

これは、標準時間ならどの時間にも変換することが可能だからです。

 

ここまでくればユーザモデルのカスタマイズは完了です。

 

AbstractBaseUser編

Managerに関してですが、同じで大丈夫ですので再利用します。

models.pyが大きく変わります。

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

from .managers import CustomUserManager
# ユーザ情報
class Accout(AbstractBaseUserPermissionsMixin):

    email = models.EmailField(unique=True)
    first_name = models.CharField(max_length=30blank=True)
    last_name = models.CharField(max_length=150blank=True)

    is_active = models.BooleanField(
        default=True,
        help_text=_(
            "利用状況フラグ 1:利用中 0:利用不可"
        ),
    )

    created_at = models.DateTimeField(default=timezone.now)
    updated_at = models.DateTimeField(auto_now=True)

    objects = CustomUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = 

    class Meta:
        db_table="accout"

    # 返値にフルネームを作成して返す
    def get_full_name(self):
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

 

今回はAccountにしているのでsettings.pyは

# Application definition
AUTH_USER_MODEL = 'v1.Account'

になります。

 

あとは 

python manage.py makemigrations
python manage.py migrate

を実行してマイグレーションを実行しましょう。

 

確認のために

python manage.py createsuperuser

を行って、Accountテーブルにレコードが増えているか確認しましょう。