このページでは、Django における『カスタムユーザー
』について解説していきます。
カスタムユーザー
を導入することで、以降で解説する『ログイン機能』をウェブアプリで簡単に実現することができるようになります。
この Django 入門 の連載では、連載を通して掲示板アプリを開発していっています。この掲示板アプリでも User
というモデルクラスを定義していますが、この User
では、ユーザー名・メールアドレス・年齢の3つのフィールドのみを扱うようになっています。
この掲示板アプリは Django 入門者の方向けに、学習用に作成しているウェブアプリなので、上記のような User
でユーザーを管理するのでも問題ないのですが、本格的なウェブアプリを開発してい行く上では、ユーザーを管理するモデルクラスとしては上記のような User
では不十分です。
例えば、ウェブアプリにはログイン機能を備えたものが多いです。ログインは、ユーザーに設定されたパスワードに基づいて実施されるため、ユーザーを管理するモデルにはパスワード管理機能が必要となります。また、ユーザーに応じて利用可能な機能を制限するため、ユーザーに権限を設定できるよう、権限管理機能も備えておく必要があります。
こういった、ウェブアプリを開発していく上で必要となるユーザー管理モデルを実現するためのモデルクラスが、今回紹介する カスタムユーザー
になります。
今回、この カスタムユーザー
は AbstractUser
というクラスから作成することを前提に解説を進めます。他にも AbstractBaseUser
というクラスから作成することもでき、それに関しては下記ページで解説していますので、こちらについて知りたい方は別途下記ページをご参照いただければと思います。
また、このページは Django 入門 の連載を読み進めてきていただいた方向けの解説を行います。連載とは分離する形で、個別にカスタムユーザーのみについて解説するページも下記に用意していますので、カスタムユーザーについてのみ知りたい方は、下記ページの方を読んでいただくことをオススメします。
【Django】カスタムユーザー(独自のユーザー)の作り方【AbstractUser編】カスタムユーザー
の基本
では、ここから、カスタムユーザー
の基本的な事柄について解説していきます。
カスタムユーザー
とは
カスタムユーザー
とは、ユーザーを管理するモデルの1つで、Django フレームワークで定義される AbstractUser
というモデルクラスを継承し、ウェブアプリに合わせてカスタマイズしたクラスのことを言います。
この AbstractUser
は、下の図のように AbstractBaseUser
と PermissionsMixin
を継承するクラスで、簡単に言えば、AbstractBaseUser
はパスワードに関する基本的な機能を備えたクラス、PermissionsMixin
は権限に関する基本的な機能を備えたクラスになります。
なので、これらを継承する AbstractUser
は、パスワードや権限に関する基本的な機能を備えたクラスとなります。そして、この AbstractUser
を継承して カスタムユーザー
を定義することになるため、カスタムユーザー
に関してもパスワードや権限に関する基本的な機能を備えたクラスとなります。
分かりやすい例で言えば、パスワード管理は、ウェブアプリでのログインを実現するために必要となる機能です。そして、カスタムユーザー
は、このパスワードの管理機能を備えていますので、カスタムユーザー
を利用することで簡単にログインを実現することができるようになります。権限に関しては、例えば管理画面へのログインの可否や管理画面での各種操作の利用の可否を判断するために用いられます。
特に権限に関しては、下記ページの管理画面での解説の中で詳細を解説しようと思います。
【Django入門11】管理画面(admin)の使い方の基本また、AbstractUser
は username
(ユーザー名) や password
(パスワード) など、ユーザーを管理する上で必要となる基本的なフィールドも保持しています。なので、AbstractUser
を継承することで、カスタムユーザー
でも、これらのフィールドを利用することが可能です。
こんな感じで、カスタムユーザー
とは、ユーザーを管理する上で必要となる基本的な機能やフィールド等を持つクラスとなります。ただし、AbstractUser
には、一般的なウェブアプリで必要となる共通的な機能やフィールド・メソッドしか定義されていません。そのため、カスタムユーザー
は AbstractUser
を単に継承するだけでなく、開発するウェブアプリで扱うユーザーに合わせたカスタマイズが必要となります。例えばフィールドを追加したりメソッドを追加したりすることが必要です。このあたりは後述で解説していきたいと思います。
スポンサーリンク
カスタムユーザー
はモデルクラスの一種
また、AbstractUser
や AbstractBaseUser
は Model
のサブクラスの1つとなります。なので、これらを継承する カスタムユーザー
も Model
のサブクラスであり、下記ページで解説しているモデルクラスであることになります。
カスタムユーザー
クラスはテーブル
したがって、カスタムユーザー
は、上記のようなパスワードや権限に関する機能だけでなく、データベースの操作機能も持っています。
また、モデルクラスはデータベースのテーブルに、そして、モデルクラスのインスタンスはレコードに相当するため、カスタムユーザー
クラスは「ユーザーを管理するためのテーブル」で、カスタムユーザー
のインスタンスは「そのテーブルのレコード」として扱うことができます。さらに、このレコードにはユーザーの情報がセットされていますので、インスタンスをユーザーそのもののように扱うことができます。
このように、カスタムユーザー
は、通常のモデルクラスと同じような使い方も可能であることは覚えておくとよいと思います。
Model
と Model のサブクラス
との違い
ここで、少し話は逸れますが、Model
と AbstractUser
のような Model のサブクラス
との違いについて解説しておきます。
Model
は、(作り込むことで)どんなデータでも管理できるように定義された超抽象的なクラスになります。なので、もちろん Model
を継承したクラスでユーザーを管理することも可能です。ですが、その管理対象に合わせたカスタマイズ(フィールドやメソッド等の追加)が別途必要になります。
これに対し、Model のサブクラス
は、何らかのデータを管理できるように特化されており、そのデータを管理できるように既にカスタマイズされたクラスになります。なので、ウェブアプリで管理したいデータに特化した Model のサブクラス
が存在するのであれば、それを継承してクラスを定義した方が楽に目的のクラスを実現できることになります。
前述の通り、AbstractUser
はユーザーを管理することに特化した Model のサブクラス
ですので、ユーザーを管理するモデルクラスを定義するのであれば、Model
を継承するのではなく、AbstractUser
を継承する方が良いです。AbstractUser
の場合は、継承するだけでユーザーを管理するモデルクラスが実現できます。
User
クラスとの使い分け
実は、Django フレームワークでは AbstractUser
や AbstractBaseUser
だけでなく、User
というクラスも定義されています。そして、この User
も AbstractUser
を継承するクラスになります。したがって、この User
にもパスワードや権限に関する機能が備えられていることになります。
この User
は、ページの最初に紹介した掲示板アプリの User
とな異なるクラスとなります
なので、ユーザーを管理するモデルクラスとして、この User
を使用するというのも1つの手になると思います。
ただ、この User
は「一般的なウェブアプリ向けのユーザー」として既に完成しているモデルクラスとして扱われることが多いです。なので、User
を利用するのであれば、カスタマイズ等を行わずにそのまま使うのが一般的です。
逆に、開発するウェブアプリ向けにカスタマイズを行うのであれば、カスタムユーザー
を別途定義し、その カスタムユーザー
をユーザーを管理するモデルクラスとして利用することが推奨されています。そして、ユーザー管理モデルが持つフィールドや機能等はウェブアプリの特徴に直結することも多いため、カスタマイズが必要になることを考慮して User
は利用せず、カスタムユーザー
を利用するのが良いと思います。
もちろん、お試しでウェブアプリを開発する場合などは User
を利用するのでも良いです。
カスタムユーザー
の作り方
続いて、カスタムユーザー
の作り方(定義の仕方)について解説していきます。
前述のとおり、カスタムユーザー
はモデルクラスであるため、通常のモデルクラスと同様に models.py
に定義を行うことになります。
スポンサーリンク
AbstractUser
を継承する
また、これも前述のとおり、カスタムユーザー
は AbstractUser
のサブクラスとして定義します。
この AbstractUser
は django.contrib.auth.models
で定義されているため、models.py
を下記のように実装すれば、カスタムユーザー
である CustomUser
が定義できることになります。
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
pass
非常に簡単な定義になりますが、これでも CustomUser
には、パスワードや権限に関連する機能やユーザーに関するフィールドが備えられており、ユーザーを管理するための基本的な機能の備わったモデルクラスとして扱うことができます。
また、上記のように定義した CustomUser
は前述で示した User
と機能的には同等のモデルクラスとなります。
プロジェクトで使用する「ユーザー管理モデル」に設定する
カスタムユーザー
を定義したら、次はプロジェクトで使用する「ユーザー管理モデル」として、定義した カスタムユーザー
を設定する必要があります。
プロジェクトの「ユーザー管理モデル」とは
Django では、プロジェクト(ウェブアプリ)で使用する「ユーザー管理モデル」を設定できるようになっています。例えば、管理画面にログインするときには、このユーザー管理モデルとして設定されているモデルクラスのテーブルが参照されますし、後述で紹介するコマンドでのユーザー作成時には、このユーザー管理モデルとして設定されているモデルクラスのインスタンスが生成されるようになっています。
そして、デフォルトでは、プロジェクトで使用するユーザー管理モデルとして User クラスとの使い分け で紹介した User
が設定されています。新たに定義した カスタムユーザー
がプロジェクトで使用されるようにするためには、この設定を変更する必要があります。
プロジェクトで使用する「ユーザー管理モデル」の設定方法
プロジェクトで使用するユーザー管理モデルに カスタムユーザー
を設定するためには、プロジェクトの settings.py
に AUTH_USER_MODEL
の設定を追記する必要があります(ファイルの一番下に追記するので問題ありません)。
AUTH_USER_MODEL = 'アプリ名.カスタムユーザー名'
アプリ名
には、カスタムユーザー
を定義した models.py
が所属するアプリの名前を、カスタムユーザー名
には カスタムユーザー
のクラス名を指定します。例えば、アプリの名前が forum
で、カスタムユーザー
のクラス名が CustomUser
であるのであれば、プロジェクトの settings.py
に下記を追記すればよいことになります。
AUTH_USER_MODEL = 'forum.CustomUser'
カスタムユーザー
を定義したにもかかわらず、上記のような設定を行わなかった場合、マイグレーション実行時に次のような例外が発生することになるので注意してください。
ERRORS: auth.User.groups: (fields.E304) Reverse accessor 'Group.user_set' for 'auth.User.groups' clashes with reverse accessor for 'forum.CustomUser.groups'. HINT: Add or change a related_name argument to the definition for 'auth.User.groups' or 'forum.CustomUser.groups'. ~略~
これは、リレーション設定用のフィールド追加時に指定する related_name
の値が User
と CustomUser
で重複しているという例外になります。User
と CustomUser
の両方に対してマイグレーションが行われると上記のような例外が発生することになり、User
に対してマイグレーションが行われるのは AUTH_USER_MODEL
に User
が指定されているからになります。
なので、AUTH_USER_MODEL
に User
以外を指定してやれば、User
に対するマイグレーションが行われなくなり、この例外も発生しなくなります。
逆に、ユーザー管理モデルを User
のままにするのであれば、カスタムユーザー
は定義してはいけないということになります
ということで、カスタムユーザー
を定義したら、settings.py
への AUTH_USER_MODEL
の設定の追記を忘れないようにしましょう。
また、以降では、プロジェクトで使用するユーザー管理モデルのことを、単に AUTH_USER_MODEL
と記します。
フィールド・メソッドを追加する
AbstractUser
のサブクラスとして カスタムユーザー
を定義し、さらに AUTH_USER_MODEL
の設定が完了すれば、後は必要に応じて開発するウェブアプリ向けに カスタムユーザー
のカスタマイズを行っていけば良いだけになります。
このカスタマイズで行うことの1つは、AbstractUser
に不足しているフィールドの追加・メソッドの追加になります。そして、このフィールドの追加に関しては通常のモデルクラス同様のやり方で、メソッドの追加に関しては一般的なクラス同様のやり方で実現できます。
例えば、先ほど定義した CustomUser
に age
フィールドと、そのユーザーが未成年であるかどうかを判断する is_minor
メソッドを追加する例は下記のようになります。
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
age = models.IntegerField()
def is_minor(self):
return True if self.age >= 18 else False
もちろん、フィールドの追加によってリレーションを設定したり、バリデーターを設定したりすることも可能です。前述のとおり、カスタムユーザー
はモデルクラスなので、モデルクラスで実現できることは カスタムユーザー
でも実現可能です。
また、メソッドのオーバーライドも可能なのですが、基本的にはユーザー管理を実現するために必要なメソッドばかりなので、オーバーライドを行う機会は少ないですし、やるとしても AbstractUser
の作りをしっかり理解してからの方が良いと思います。
参考:カスタムユーザー
の持つフィールド
参考に、カスタムユーザー
がデフォルトで持つフィールド、すなわち AbstractUser
の持つフィールドをまとめた表を下記に示しておきます。ウェブアプリを開発するために、下記に足りないフィールドがあるのであれば、それをフィールドとして定義して追加してやれば良いことになります。
フィールド名 | 役割 |
id |
ID (プライマリーキー) |
password |
パスワード |
last_login |
最終ログイン日時 |
is_superuser |
スーパーユーザーであるか否か |
username |
ユーザー名 |
first_name |
ファーストネーム |
last_name |
ラストネーム |
email |
メールアドレス |
is_staff |
管理スタッフであるか否か |
is_active |
アクティブユーザーであるか否か |
date_join |
ユーザー作成日時 |
groups |
ユーザーが所属するグループ |
user_permissions |
ユーザーの持つ権限 |
スポンサーリンク
特殊フィールドの設定を行う
また、必要に応じて、特殊フィールドの設定を行うことも可能です。
AbstractUser
においては、下記の3つのクラス変数が定義されており、これらのクラス変数に指定されているフィールドは、少し特殊な意味を持つフィールドとして扱われます。
USERNAME_FIELD
EMAIL_FIELD
REQUIRED_FIELDS
具体的には、 USERNAME_FIELD
に指定されたフィールドは『ユーザーを識別するフィールド』、EMAIL_FIELD
に指定されたフィールドは『メールアドレスとして扱うフィールド』を 、REQUIRED_FIELDS
に指定されたフィールドは『入力受付を必須とするフィールド』として扱われることになります。
そして、AbstractUser
では、これらのクラス変数は下記のように定義されています。
EMAIL_FIELD = "email"
USERNAME_FIELD = "username"
REQUIRED_FIELDS = ["email"]
AbstractUser
を継承する カスタムユーザー
においても上記のクラス変数が引き継がれることになりますが、カスタムユーザー
側でクラス変数を再定義することで、これらのクラス変数に指定するフィールドを変更することが可能で、これにより カスタムユーザー
のカスタマイズを行うことができます。
特に、変更する機会が多いのは USERNAME_FIELD
と REQUIRED_FIELDS
になりますので、この2つについてクラス変数の意味合いや変更例等についての詳細を解説していきます。
USERNAME_FIELD
USERNAME_FIELD
は『ユーザーを識別するフィールド』を設定するクラス変数で、この『ユーザーを識別するフィールド』とは、主にログイン等で使用する、パスワードとは異なる方のフィールドのことになります。
ウェブアプリでは、ログイン時にパスワードだけでなく、『ユーザーを識別するフィールド』の入力が必要です。『ユーザーを識別するフィールド』 はウェブアプリによって違っていて、ユーザー名を入力してログインを行うウェブアプリもありますし、メールアドレスを入力してログインを行うウェブアプリもあります。
このように、ウェブアプリによって『ユーザーを識別するフィールド』として使用するフィールドは異なることになり、カスタムユーザー
において、この『ユーザーを識別するフィールド』の設定を行うためのクラス変数が USERNAME_FIELD
となります。
また、USERNAME_FIELD
で設定するフィールドは、上記のようにログイン時に入力するフィールドとしても利用されますし、カスタムユーザー
のインスタンスを出力したときに表示されるデータも、この USERNAME_FIELD
で指定されたフィールドの値となります。つまり、__str__
メソッドの返却値は、この USERNAME_FIELD
に指定されたフィールドの値ということになります。
__str__
メソッドに関しては下記ページで解説していますので、詳しく知りたい方は別途下記ページを参照してください。
USERNAME_FIELD
にはデフォルトで "username"
が設定されていますので、ログイン時に入力受付を行うフィールドや、インスタンス出力時に表示されるフィールドを username
以外のものに変更したいのであれば、USERNAME_FIELD
の変更を行う必要があります。
そして、この USERNAME_FIELD
は、カスタムユーザー
で USERNAME_FIELD
を定義することで変更することが可能です。例えば、下記のように カスタムユーザー
で USERNAME_FIELD
を定義すれば nickname
でログインできるようになりますし、インスタンスの出力結果が nickname
の値に変化することになります。
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
nickname = models.CharField(max_length=32, unique=True)
USERNAME_FIELD = 'nickname'
ただし、USERNAME_FIELD
に設定するフィールドには unique=True
が設定されている必要があるので注意してください。具体的には、これに設定するフィールドを定義するときに実行する Field
のサブクラスのコンストラクタに引数 unique=True
を設定しておく必要があります。この unique=True
は、そのフィールドの値が他のレコードと重複することを禁止するための設定となります。
REQUIRED_FIELDS
REQUIRED_FIELDS
は『入力受付を必須とするフィールド』を設定するクラス変数です。
この設定は、コマンドでのユーザー作成時(カスタムユーザー
のインスタンス作成時)に参照される設定になります。Django のコマンドには createsuperuser
が存在し、このコマンドを実行して必要なフィールドの情報を入力すれば “スーパーユーザー” を作成することができます。で、このコマンドで入力受付されるフィールドは下記の3種類のものになります
USERNAME_FIELD
で指定されたフィールドREQUIRED_FIELDS
で指定されたフィールド群password
(確認用のパスワードの入力受付も行われる)
上記のように REQUIRED_FIELDS
が参照されるので、REQUIRED_FIELDS
を変更することで、コマンドでのユーザー作成時に入力受付するフィールドを変更することができます。
ポイントは、カスタムユーザー
に追加した “必須フィールド” も、コマンドでのユーザー作成時に入力受付する必要があるという点になります。
モデルクラスに持たせたフィールドは、そのフィールドに null=True
オプションを指定しないと必須フィールドとして扱われることになります(必須フィールドとは、より具体的には、テーブルに保存する際に空であってはならないフィールドのことを指しています)。
たとえば、下記の CustomUser
では age
フィールドを追加しており、このフィールドは null=True
オプションが指定されていないため、必須フィールドとして扱われます。必須フィールドに値が設定されていないインスタンスでは、テーブル保存時に例外が発生することになります。
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
age = models.IntegerField()
実際に、この age
フィールドに値を設定していないインスタンスをテーブルに保存しようとすると、下記のような例外が発生することになります。
django.db.utils.IntegrityError: NOT NULL constraint failed: forum_customuser.age
なので、こういった必須フィールドを カスタムユーザー
に追加するのであれば、そのフィールドの値をフォームから送信できるようにフォームクラスを定義する必要がありますし、同様に、コマンドでユーザーを作成する際にも、必須フィールドの値を指定できるようにする必要があります。
そして、特にコマンドでユーザーを作成する際には REQUIRED_FIELDS
に含めたフィールドの入力受付が行われることになりますので、REQUIRED_FIELDS
に カスタムユーザー
の必須フィールドを全て指定する必要があります。もしくは、カスタムユーザー
の必須フィールドを「非必須フィールド」に変更する (null=True
オプションを追加する) or 必須フィールドのデフォルト値を指定しておく必要があります。
例えば、先ほど示した CustomUser
の例であれば、コマンドでのユーザー作成を行えるようにするためには下記のように REQUIRED_FIELDS
の定義を追加してやれば良いことになります。
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
age = models.IntegerField()
REQUIRED_FIELDS = ['email', 'age']
もしくは、下記のように age
フィールドに null=True
オプションを指定し、age
フィールドの値が指定されていない状態でもテーブルへの保存が行えるようにすることで、コマンドでのユーザー作成を実現することもできます。
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
age = models.IntegerField(null=True)
REQUIRED_FIELDS
は AbstractUser
のフィールドに合わせて設定されていますので、カスタムユーザー
側でフィールドを追加した場合は、REQUIRED_FIELDS
の見直し・変更が必要となります。
また、REQUIRED_FIELDS
には USERNAME_FIELD
に指定したフィールドは含められないので注意してください。
カスタムユーザー
の使い方
続いて、カスタムユーザー
の使い方について説明していきます。
基本的な使い方は他のモデルクラスと同じ
カスタムユーザー
はモデルクラスの一種なので、基本的には他のモデルクラスと同じようにビューやテンプレートファイルから扱うことが可能です。特に、データベース関連の操作は、他のモデルクラスとほぼ同じと考えて良いです。このあたりに関しては、後述の 掲示板アプリでカスタムユーザーを利用してみる で実例で確認していきたいと思います。
あとは、カスタムユーザー
に追加したフィールドやメソッドを上手く使ってビューで機能を実現していけばよいことになります。
ただ、カスタムユーザー
ならではの使い方も存在しますので、このあたりについて、ここから解説していきたいと思います。
スポンサーリンク
カスタムユーザー
を get_user_model
で取得する
Django フレームワークの django.contrib.auth
には get_user_model
という関数が定義されており、この関数を実行することで AUTH_USER_MODEL
のクラスオブジェクトを返却値として取得することができるようになっています。
プロジェクトで使用する「ユーザー管理モデル」に設定する で解説したように、この AUTH_USER_MODEL
には、カスタムユーザー
を定義している場合は カスタムユーザー
を設定しているはずなので、要は get_user_model
を実行することで カスタムユーザー
のクラスオブジェクトを取得することができるということになります。
そして、views.py
や forms.py
等から get_user_model
を実行して カスタムユーザー
のクラスオブジェクトを取得し、このオブジェクトからテーブル操作を行ったり、インスタンスを生成したりすることが可能となります。
例えば、下記は views.py
で get_user_model
を実行し、それによって得られたオブジェクトから カスタムユーザー
のテーブルの全レコードを取得する例になります。
from django.contrib.auth import get_user_model
from django.shortcuts import render, get_object_or_404
# カスタムユーザーを取得
User = get_user_model()
def users_view(request):
users = User.objects.all()
context = {
'users' : users
}
return render(request, 'テンプレートファイルのパス', context)
もちろん、get_user_model
を使用せず、他のモデルクラスと同様に、models.py
から カスタムユーザー
を import
して使用するのでも良いです。ですが、get_user_model
を利用した方がウェブアプリのメンテナンス性が高くなります。
具体的にいうと、get_user_model
を利用した場合、後で カスタムユーザー
のクラス名を変更した場合に修正が必要となる箇所は settings.py
の AUTH_USER_MODEL
設定箇所のみとなります。
ですが、models.py
から カスタムユーザー
を import
して使用していた場合、カスタムユーザー
のクラス名を変更した際には、その import
を行っている個所の全てを修正する必要がありますし、変更前の カスタムユーザー
のクラス名を記述している個所も全て修正が必要になります。特に、ユーザー管理用のモデルクラスは様々なモジュールから利用されることになるため、この場合は修正が非常に大変になりますし、修正漏れがあるとバグになります。
そのため、メンテナンス性の高いウェブアプリを開発していく上では、AUTH_USER_MODEL
、すなわち カスタムユーザー
は、 get_user_model
から取得して使用するようにした方が良いです。
カスタムユーザー
をベースにフォームを定義する
また、前述のとおり、カスタムユーザー
は他のモデルクラスと同様に利用することができ、下記ページで解説しているモデルフォームクラスのベースとして指定することも可能です(model
属性に指定可能)。また、モデルフォームクラスは、ModelForm
クラスを継承することで定義することが可能です。
なんですが、カスタムユーザー
を作成するモデルフォームクラスを定義する際には、ModelForm
クラスを継承するのではなく、UserCreationForm
を継承することをオススメします。
UserCreationForm
とは
UserCreationForm
は ModelForm
のサブクラスであり、UserCreationForm
を継承するクラスも通常のモデルフォームクラスと同様にして定義することができます。
より具体的には、UserCreationForm
のサブクラスとして定義し、model
属性でベースとするモデルクラスを、fields
属性(or exclude
属性)でフォームに持たせるフィールドを指定してクラスを定義することになります。で、カスタムユーザー
を作成するフォームとするために、model
属性には カスタムユーザー
を指定することになります。
ただ、ModelForm
を継承したクラスによって実現されるフォームに比べ、UserCreationForm
を継承したクラスによって実現されるフォームには下記の違いがあります。
- パスワードフィールド(確認用パスワードも含めて)が自動的に追加される
- パスワードフィールドへの入力文字は伏字になる
- パスワードフィールドへ入力されたパスワードは、暗号化してデータベースに保存される
UserCreationForm
を継承するメリット
最も重要なのは最後の1つで UserCreationForm
を継承することで、パスワードを暗号化したデータ(ハッシュ化されたデータ)で扱うことができるようになります。ModelForm
においても、パスワードフィールドを追加するようクラスを定義すればパスワードの入力自体は受け付けることができるようになります。ですが、基本的には、そのフィールドに入力されたパスワードは平文で扱われることになり、データベースにも平文で保存されることになります。
したがって、もしデータベースの中身を他の人に見られたら、ユーザーのパスワードが流出することになります。こうなるとニュースで取りあげられるくらい大問題になります…。自力で暗号化を行うようなウェブアプリを開発するという手もありますが、暗号に関する深い知識も必要になりますし、上手く作らないと、結局パスワードの流出のような問題が発生する可能性があります。
それに対し、UserCreationForm
を継承したクラスの場合、パスワードフィールドへ入力されたパスワードが暗号化され、暗号化された状態でデータベースに保存されることになります。なので、たとえデータベースの中身が見られたとしても、理論的には暗号化前のパスワードが流出することはありません。つまり、UserCreationForm
を継承したクラスを利用するだけで、安心してパスワードを扱うことのできるウェブアプリが実現できます。
ウェブアプリではパスワードは暗号化した状態で扱うというのが大前提になります。なので、パスワードの入力受付を行うフォームを作るのであれば、必ず UserCreationForm
のようなパスワードを暗号化してくれるクラスを利用した方が良いです。
ということで、カスタムユーザー
でパスワードを扱うのであれば、必ずユーザー作成用のフォームは UserCreationForm
を継承して定義するようにしましょう。
また、こういった、暗号化のような難しい技術も簡単に利用できるという点は、Django を含むフレームワークを利用する大きなメリットだと言えます。
UserCreationForm
を継承したフォームの定義例
UserCreationForm
を継承する カスタムユーザー
作成用のモデルフォームクラスの定義例は下記のようになります。
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm
User = get_user_model()
class CreateUserForm(UserCreationForm):
class Meta:
model = User
fields = [User.USERNAME_FIELD] + User.REQUIRED_FIELDS
fields = [User.USERNAME_FIELD] + User.REQUIRED_FIELDS
を指定しておくことで、カスタムユーザー
の持つフィールドの中の “入力受付が必須となるフィールド” を全て含むモデルフォームクラスが定義できることになります(カスタムユーザー
の定義で、USERNAME_FIELD
と REQUIRED_FIELDS
を適切に設定しておく必要があります)。
また、上記の User
は get_user_model
関数の返却値なので カスタムユーザー
を指すクラスオブジェクトということになります。
上記のように定義を行えば、入力受付が必須となるフィールドを全て含む カスタムユーザー
作成用のフォームが定義できることになりますので、まずは カスタムユーザー
を作成するフォームは、上記の CreateUserForm
と同じ形式で定義してやれば良いと思います。その後に、必要に応じてカスタマイズを行っていけばよいです。
ちなみに、上記の CreateUserForm
をテンプレートファイルに埋め込むことで表示されるフォームは下図のようなものになります(スタイル等の設定を行うことで、もっと見た目を良くすることは可能です)。
createsuperuser
コマンドでのユーザーを作成する
特殊フィールドの設定を行う でも少し説明しましたが、Django には createsuperuser
コマンドが存在し、このコマンドで、ウェブアプリのスーパーユーザーを作成することができます。スーパーユーザーとは、簡単に言えば全ての管理権限を持ったユーザーです。
管理画面については別途下記ページで解説を行いますが、管理画面とはウェブアプリで扱う各種テーブルのレコードの管理を行う画面であり、この管理画面にログイン可能なユーザーを作成するときに、この createsuperuser
コマンドを利用することが多いです。
で、この createsuperuser
コマンドを実行した際には カスタムユーザー
のインスタンスが生成されることになります。もう少し厳密に言うと、settings.py
の AUTH_USER_MODEL
に設定されたモデルクラスのインスタンスが生成されることになります。
なので、カスタムユーザー
はフォーム等から作成するだけでなく、コマンドでも作成できることになります。具体的には、下記のように manage.py
を利用して createsuperuser
コマンドを実行し、”各種フィールド” の値を入力してやれば カスタムユーザー
のインスタンスが生成され、データベースの カスタムユーザー
のテーブルに保存されることになります。
python manage.py createsuperuser
また、上記の “各種フィールド” とは、特殊フィールドの設定を行う でも説明した下記の3種類のフィールドとなります。
USERNAME_FIELD
で指定されたフィールドREQUIRED_FIELDS
で指定されたフィールド群password
(確認用のパスワードの入力受付も行われる)
そして、これも 特殊フィールドの設定を行う で説明した通り、カスタムユーザー
における必須フィールドが全て入力できるように REQUIRED_FIELDS
を定義しておくことが重要となります。
ここで説明しておきたいのは、今までは models.py
で定義したモデルクラスは基本的にプロジェクト内のファイル(views.py
・forms.py
・テンプレートファイル等)のみからしか使用されていませんでしたが、カスタムユーザー
は、それ以外のファイル、すなわち Django フレームワークからも利用されることになるという点になります。そして、その1つの例が、上記における createsuperuser
コマンドからの カスタムユーザー
の利用になります。
したがって、Django フレームワークからの使われ方も意識して カスタムユーザー
を定義する必要があります。カスタムユーザー
の定義が、Django フレームワークからの使われ方に適合していない場合は例外が発生するはずなので、その例外の内容に合わせて カスタムユーザー
を修正する必要があります。
スポンサーリンク
掲示板アプリでカスタムユーザーを利用してみる
では、いつも通り、ここまで説明してきた内容を踏まえて、実際に カスタムユーザー
の利用例を示していきたいと思います。
この Django 入門 に関しては連載形式となっており、ここでは前回下記ページの 掲示板アプリでモデルフォームを利用してみる で作成したウェブアプリに対して カスタムユーザー
を導入する形で、カスタムユーザー
の利用例を示していきたいと思います。
カスタムユーザー
を定義することで、ウェブアプリ内でパスワード管理や権限関連の機能を備えたユーザーを使用することができるようになります。ただ、今回は単に カスタムユーザー
を導入するだけなので、特にこれらのフィールドや機能は意味を持ちません。
ですが、次回の連載でログイン機能について説明を行い、そこで カスタムユーザー
を利用したログインの実装を行っていくことになります。そして、そこでは カスタムユーザー
のパスワード管理機能が活躍することになります。なので、今回は、次回の説明に向けた準備と考え、まずは カスタムユーザー
について理解を深めていただければと思います。
掲示板アプリのプロジェクト一式の公開先
この Django 入門 の連載を通して開発している掲示板アプリのプロジェクトは GitHub の下記レポジトリで公開しています。
https://github.com/da-eu/django-introduction
また、前述のとおり、ここでは前回の連載の 掲示板アプリでモデルフォームを利用してみる で作成したプロジェクトをベースに変更を加えていきます。このベースとなるプロジェクトは下記のリリースで公開していますので、必要に応じてこちらからプロジェクト一式を取得してください。
https://github.com/da-eu/django-introduction/releases/tag/django-modelform
さらに、ここから説明していく内容の変更を加えたプロジェクトも下記のリリースで公開しています。ソースコードの変更等を行うのが面倒な場合など、必要に応じて下記からプロジェクト一式を取得してください。
https://github.com/da-eu/django-introduction/releases/tag/django-customuser
カスタムユーザー
の定義
では、早速 カスタムユーザー
を定義していきたいと思います。
これまでの掲示板アプリにおいては、単なるモデルクラスとして下記の User
を定義していました。
class User(models.Model):
username = models.CharField(max_length=32, validators=[check_username])
email = models.EmailField()
age = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(200)])
def __str__(self):
return self.username
今回は、この User
を削除し、代わりに カスタムユーザー
として CustomUser
クラスを定義していきたいと思います。ただし、もともとの User
の持っていたフィールド username
・email
・age
は CustomUser
にも持たせるようにしたいと思います。
このような CustomUser
は、下記のように models.py
を変更することで定義することができます(Comment
に関しては、ForeignKey
の第1引数の User
から CustomUser
への変更のみを行っています)。
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.core.validators import MinValueValidator, MaxValueValidator
class CustomUser(AbstractUser):
age = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(200)])
REQUIRED_FIELDS = ['email', 'age']
class Comment(models.Model):
text = models.CharField(max_length=256)
date = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True, related_name='comments')
def __str__(self):
return self.text[:10]
参考:カスタムユーザー の持つフィールド で示したように、AbstractUser
には username
と email
が存在し、それらは CustomUser
にも引き継がれることになるため、CustomUser
では username
と email
の定義は不要になります。上記のように models.py
を変更すると、もともとの User
の username
に対して実施されていた妥当性の検証が行われなくなってしまいますが、AbstractUser
の username
に対しても妥当性の検証が行われるようになっているので、それをそのまま適用したいと思います。
また、もともとの User
では __str__
メソッドを定義し、インスタンスの出力時に username
フィールドを出力するようにしていましたが、AbstractUser
でも同様の __str__
メソッドが定義されているため、 CustomUser
では __str__
メソッドをオーバーライドしないようにしています。
正確に言うと、AbstractUser
の __str__
メソッドでは、USERNAME_FIELD
に指定されているフィールドの値が出力されるようになっています
ただし、USERNAME_FIELD
には 'username'
が指定されているので、CustomUser
でも User
同様の出力結果が得られることになります
さらに、age
フィールドは、今までも必須フィールドとして扱っていたため、今回も必須フィールドとして定義するようにしています。そのため、createsuperuser
コマンドでユーザーの作成ができるよう、REQUIRED_FIELDS
には 'age'
を追加しています。
スポンサーリンク
AUTH_USER_MODEL
の設定
カスタムユーザー
を定義した後には、プロジェクトで使用する「ユーザー管理モデル」の設定、すなわち AUTH_USER_MODEL
の設定を忘れずに行うようにしましょう!
今回は、forum
アプリに カスタムユーザー
として CustomUser
を定義していますので、プロジェクトの settings.py
の最後に下記を追記すればよいことになります。
AUTH_USER_MODEL = 'forum.CustomUser'
これにより、プロジェクト内ではユーザー管理モデルとして CustomUser
が利用されることになり、管理画面へのログインや createsuperuser
コマンドでのユーザー作成時に CustomUser
が利用されることになります。さらに、get_user_model
関数を実行すれば CustomUser
が取得できるようにもなります。
カスタムユーザー
の登録フォームの定義
続いて、ユーザー登録フォームの変更を行っていきます。
これまでは、下記のように、User
のインスタンスを作成するためのフォームとして RegisterForm
を定義していました。そして、この RegisterForm
はモデルフォームクラスであり、ベースとなるモデルクラスとして User
を指定するようにしていました。また、この RegisterForm
では、username
入力用フィールド、email
入力用フィールド、age
入力用フィールドを持つようにしていました。
class RegisterForm(forms.ModelForm):
class Meta:
model = User
fields = ['username', 'email', 'age']
これからは CustomUser
を「ユーザーを管理するモデルクラス」として使用することになるため、RegisterForm
を CustomUser
作成用のフォームに変更していきたいと思います。さらに、今回完成するウェブアプリでは不要ではあるのですが、今後ログイン機能等を追加していくことを見越し、パスワードも入力可能なフォームに仕立てていきたいと思います。
そして、このようなフォームは、カスタムユーザー をベースにフォームを定義する で説明した内容にしたがって UserCreationForm
のサブクラスを定義することで実現できます。
具体的には、forms.py
を下記のように変更してやれば、上記のような「CustomUser
作成用のフォーム」として RegisterForm
を定義することができます(今回は PostForm
の変更は不要です)。
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm
from .models import Comment
User = get_user_model()
class RegisterForm(UserCreationForm):
class Meta:
model = User
fields = [User.USERNAME_FIELD] + User.REQUIRED_FIELDS
class PostForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['user', 'text']
widgets = {
'text': forms.Textarea
}
ビューの変更
最後にビューを、User
ではなく CustomUser
を利用するように変更していきます。
基本的な使い方は他のモデルクラスと同じ で説明したように、カスタムユーザー
の使い方は、基本的には他のモデルクラスと同じです。なので、今までの views.py
で User
と記述している部分を CustomUser
に置き換えてやるだけで、User
ではなく CustomUser
を利用するビューを実現することができます。
ただ、直接 CustomUser
と記述すると、またクラス名を変更したときに、同様の置き換えが必要になって面倒です。なので、get_user_model
関数を実行してクラスオブジェクトを取得するようにし、この「get_user_model
の返却値を受け取る変数」の 変数名
で User
部分を置き換えてやった方が後々の変更が楽になります。さらに、この「get_user_model
の返却値を受け取る変数」の 変数名
を User
にしてしまえば、そもそも User
部分の置き換えが不要となります。
ということで、views.py
の変更は、views.py
の先頭部分に get_user_model
の import
と User = get_user_model()
の追記、および、models.py
からの User
の import
を削除するだけで済むことになります。これで、User
ではなく CustomUser
を利用するビューが実現できます。
具体的には、もともとの views.py
の先頭部分は下記のようになっていましたが、
from django.shortcuts import redirect, render, get_object_or_404
from .forms import RegisterForm, PostForm
from .models import User, Comment
これを下記のように変更してやるだけで、User
ではなく CustomUser
を利用するビューが実現できます。
from django.shortcuts import redirect, render, get_object_or_404
from .forms import RegisterForm, PostForm
from .models import Comment
from django.contrib.auth import get_user_model
User = get_user_model()
変更前の views.py
では User.objects.all()
で User
の全インスタンスを取得したり、get_object_or_404(User, id=user_id)
で id
が user_id
と一致する User
のインスタンスを取得したり、さらには User
のインスタンスに save
メソッドを実行させてテーブルにレコードとしてインスタンスの保存を行ったりしていました。これらのテーブルやレコードの操作を User
を CustomUser
に置き換えても問題なく行うことが可能であることより、カスタムユーザー
の使い方は、基本的には他のモデルクラスと同じであることを実感していただけるのではないかと思います。
また、カスタムユーザー
の使い方が他のモデルクラスと同様であるため、テンプレートファイルに関しては今回修正は不要になります。ただし、修正が不要なのは、元々の User
が持っているフィールドを全て CustomUser
が持っているという点にも起因しています。User
が持っているフィールドを CustomUser
に持たせていないような場合は、テンプレートファイル等の変更も必要になるので注意してください。
スポンサーリンク
動作確認
以上で、今回の変更は完了となりますので、最後に今回実装した内容に関して動作確認を行なっていきましょう!
DB 関連の初期化
今回は、前回から models.py
を大幅に変更しているため、そのままマイグレーションを実行するとエラーになる可能性が高いです。そのため、マイグレーションを実行する前に、データベース関連のファイルの削除を行いたいと思います。
ここから紹介する手順でデータベースの初期化を行うとデータベースやマイグレーション用の設定ファイルを完全に初期化することができます。ただ、今まで作成してきたレコードも無くなってしまいますので、この点は注意してください。ただ、一番確実&楽にマイグレーションのエラーを解決する手順ではあるので、練習でウェブアプリを開発していてマイグレーションのエラーがどうしても解決できない場合は、この手順で初期化するのが良いと思います。
まず、ターミナルやコマンドプロンプト等のコマンド実行可能アプリを開きます。そして、プロジェクトフォルダ(testproject
フォルダ)の直下に移動してください。このフォルダには manage.py
が存在するはずで、いつもマイグレーション等のコマンドを実行しているフォルダになります。
さらに、移動後に下記コマンドを実行してデータベースファイルを削除します。
% rm db.sqlite3
続いて、下記コマンドを実行してマイグレーション設定ファイルを削除します。
% rm forum/migrations/00*
以上で、データベース関連のファイルが全て削除されたことになります。
マイグレーションの実行
続いて、マイグレーションを実行していきたいと思います。
いつも通り、testproject
フォルダの中、つまり manage.py
が存在するフォルダで下記コマンドを実行してください。
% python manage.py makemigrations
% python manage.py migrate
これにより、CustomUser
や Comment
のテーブルがデータベースに追加されることになります。
開発用ウェブサーバーの起動
マイグレーションが完了した後は、いつも通り Django 開発用ウェブサーバーの起動を行います。マイグレーションを実行した時と同じフォルダで下記コマンドを実行すれば、Django 開発用ウェブサーバーが起動します。
% python manage.py runserver
ユーザー登録フォームでのユーザー作成の確認
ここからウェブアプリの動作確認に移っていきます。
最初に、ユーザー登録フォームでユーザーの登録、すなわち カスタムユーザー
のインスタンスの追加が可能であることを確認したいと思います。
まずは、ウェブブラウザで下記の URL にアクセスしてみてください。
http://localhost:8000/forum/register/
これにより、ユーザー登録フォームが表示されると思います。注目ポイントは、RegisterForm
の fields
に指定したフィールドに加え、パスワード
及び パスワード(確認用)
フィールドが表示されている点になります。これは RegisterForm
のスーパークラスの UserCreationForm
によって追加されたフィールドになります。また、ユーザー名
や パスワード
のフィールドにヘルプテキストが表示されるようになっていますが、これは AbstractUser
や AbstractBaseUser
で、このようなヘルプテキストが表示されるようにフィールドの設定が行われているからになります。
続いて、これらの各種フィールドに適切なデータを入力した後に 送信
ボタンをクリックしてユーザー登録を実行してみてください。すると、ユーザー一覧画面に遷移し、登録したユーザーが表示されることが確認できると思います。このユーザーは CustomUser
のインスタンスで、ユーザーを扱うクラスを User
から CustomUser
に変更しましたが、今まで通りの動作が実現できていますね!
また、コメント投稿フォームでも、今まで通り投稿者のユーザーを選択してコメントが投稿可能であることが確認できると思います。
今回のアプリではパスワードを明示的に扱わないため、パスワードの入力を行ったのに意味がなかったようにも思えますが、テーブルに保存されたレコードにはしっかりパスワードが保存されています(暗号化された状態で)。そして、このパスワードは、次回の連載で導入するログイン機能で利用されることになります。
createsuperuser
コマンドでのユーザー作成の確認
次は、コマンド実行でのユーザー作成を行っていきます。runserver
コマンドを一旦強制終了し(control
+ c
で終了可能です)、次は下記のコマンドを実行してください。
python manage.py createsuperuser
コマンドを実行すると、CustomUser
の USERNAME_FIELD
と REQUIRED_FIELDS
に指定されているフィールド(username
・email
・age
)およびパスワードフィールド(確認用も含めて)の値の入力が促されるはずです。これらの入力を適切に行い、下記が出力されることを確認してください(入力時は、ユーザー名は他のユーザーとの重複は NG、パスワードは簡単すぎるものは NG であることに注意してください)。
Superuser created successfully.
上記の出力が確認できれば、再度下記コマンドで開発用サーバーを起動してください。。
% python manage.py runserver
そして、ウェブブラウザに下記 URL を指定し、再度ユーザー一覧のページを開いてみてください。
http://localhost:8000/forum/users/
すると、ユーザー一覧に createsuperuser
コマンドで作成したユーザが表示されていることが確認できると思います。
このユーザー一覧は CustomUser
の全インスタンスを表示するページですので、この表示結果より、createsuperuser
コマンドでも CustomUser
の作成が可能であることが確認できたことになります。前述のとおり、この createsuperuser
コマンドで作成されるのはスーパーユーザーで特別な権限を持ったユーザーとなります。例えば、このスーパーユーザーはウェブアプリの管理画面にログイン可能です。
管理画面へのログインの確認
ということで、最後に先ほど createsuperuser
コマンドで作成したユーザーで管理画面にログイン可能であることを確認してみましょう!
まずは開発用ウェブサーバーを起動した状態で、ウェブブラウザから下記の URL にアクセスしてみてください。
http://localhost:8000/admin/
すると、下の図のようなログインフォームが表示されるはずです。
これらのログインフォームに、先ほど createsuperuser
コマンドに指定したユーザー名とパスワードを入力し、続けて ログイン
ボタンを押してください。
すると、下の図のような管理画面にログインできるはずです。
このログイン時には、ログインフォームに入力されたパスワードと、データベースに保存されている “ログインフォームに入力されたユーザー名のレコード” のパスワードとの照合が行われています(暗号化された状態のデータで照合される)。そして、照合 OK であった場合に、ログインに成功することになります。
createsuperuser
コマンドに指定したパスワードでログインできたということは、createsuperuser
コマンド実行時に入力したパスワードがしっかりデータベースに保存されていることを、つまり、CustomUser
でのパスワード管理がバッチリ実現できていることを意味します。
ユーザー登録フォームで登録したユーザーは「一般ユーザー」となるため、ログインフォームに入力したユーザー名とパスワードがデータベースに保存されているものと一致しても管理画面にはログインできません
こんな感じで、カスタムユーザー
ではパスワード管理を行うことができ、これによりログイン機能等も実現可能です。今回は、管理画面という既に Djnago フレームワークに備えられたアプリでのログインを利用しましたが、次回の連載では、このログイン機能を、自分たちが開発するウェブアプリに搭載する手順について解説していきます。
まずは、今回説明した カスタムユーザー
についてしっかり理解しておいてください。
また、管理画面の詳細についても、後ほどの Django 入門 の連載の中で解説をしていきます。
まとめ
このページでは、Django における カスタムユーザー
について解説しました!
カスタムユーザー
は単なるモデルクラスではなく、ユーザーを管理するために必要となる基本的な機能・フィールドを備えたモデルクラスとなります。この カスタムユーザー
は、AbstractUser
というクラスを継承することで簡単に定義することが可能です。また、カスタマイズを行うことで、開発するウェブアプリに適合した カスタムユーザー
に仕立てていくこともできます。
上記のように、特別なモデルクラスではあるのですが、カスタムユーザー
はあくまでもモデルクラスの一種となりますので、カスタムユーザー
の基本的な使い方はモデルクラスと同様になります。
また、カスタムユーザー
を AUTH_USER_MODEL
として設定することで、プロジェクト内でのログインやユーザー作成が カスタムユーザー
に基づいて実行されるようになります。カスタムユーザー
を定義した後は、AUTH_USER_MODEL
の設定を忘れずに行うようにしましょう。
この カスタムユーザー
を利用するメリットが発揮されるのは、特にログイン等のパスワードを扱う機能を実現する時になります。次回の連載となる下記ページでは、そのログイン機能について解説していきますので、是非下記ページも読んでいただければと思います!