【第8回】Djangoで日記アプリを作ろう~認証機能実装編~
エンジニアになろう!
前回は Django においてテストを実施する方法を学びました。
次は、日記アプリに認証機能を付けていきましょう。
今回は、以下の内容を学んでいきます。
- Django の組み込み認証機能の概要
- 認証機能パッケージ allauth の概要
- カスタムユーザーモデルの実装
- allauth で認証機能を実装する方法
- メールをコンソールに出力する方法
それでは、Django の組み込み認証機能について学んでいきましょう!
前回の記事はこちら
Django の組み込み認証機能
Django には認証機能が標準で備わっています。
それが django.contrib.auth です。
実は、Django プロジェクトを作成した時点で自動的に組み込まれます。
(プロジェクトの作成とは、コマンドでいうと django-admin startproject のこと)
config ディレクトリ内の settings.py を見てみましょう!
INSTALLED_APPS に django.contrib.auth が既に入っているはずです。
1INSTALLED_APPS = [
2 'django.contrib.admin',
3 'django.contrib.auth', # 標準の認証機能
4 'django.contrib.contenttypes',
5 'django.contrib.sessions',
6 'django.contrib.messages',
7 'django.contrib.staticfiles',
8 'diary',
9 'bootstrap4',
10 'widget_tweaks',
11]
この django.contrib.auth ではログイン、ログアウト、パスワード変更および再設定のビューおよびフォームが提供されています。
必要最低限の機能が提供されている、といった所でしょうか。
会員制 web サービスでは必須となる「ユーザー登録」のビューおよびフォームがありません。
ユーザー登録の機能は CreateView などを利用してビューおよびフォームを実装する必要があります。
テンプレートがない
また、 django.contrib.auth のもう一つの注意点として「テンプレート」は用意されていません。
django.contrib.auth は、あくまで基本的な認証処理(=アプリケーション)を実装しているに過ぎず、画面に関しては開発者が自分で作成する必要があります。
基本的にテンプレートは作りたいサービスに依存する部分が大きいものであり、仮にテンプレートが提供されていたとしてもそのまま利用することは少ないかもしれません。
とはいえ、サンプルコードがないというのは少々辛いものです。
このように Django の標準認証機能には少々物足りない部分がありますが、それをカバーするパッケージがあります。
それが django-allauth です!
便利な認証ライブラリ django-allauth
django-allauth は認証に関する機能が網羅的に入ったパッケージです。
このパッケージを実際に使っていくのは次回の後編の記事で行うとして、今回は allauth の特徴を学んでいきましょう。
まず django-allauth が対応する認証機能ですが、django.contrib.auth が提供するログイン・ログアウトなどの最低限の機能に加えて、ユーザー登録機能のビューとフォームが含まれています。
テンプレートも提供
また各ビューに対応するテンプレートも提供されています!
django-allauth の githubページを見てみましょう。
allauth/templates/account/を見るとログインやログアウトのビューに対応するテンプレートが一通り揃っています。
英語で実装されているため、そのまま利用すると日本向けページとしては使えない部分もあります…
が、ひとまず認証機能を実装して画面遷移を確認したいという場合にはテンプレートの存在は非常にありがたいですね。
その他
他にも django-allauth には様々な強みがあります。
例えば、 github や gmail などのソーシャルアカウントの連携もサポートしていたり、ログイン失敗回数制限などのセキュリティ面も考慮されています。
allauth という名前にふさわしく、あらゆる認証機能が含まれています!
デフォルトのユーザーモデルは注意!
先ほど Django の標準認証機能と django-allauth について学びました。
認証機能について考えるときは、もう1点重要なことがあります。
それは「どのようなユーザーモデルに対して認証をかけるのか?」という点です。
日記データに対応する diary モデルと同様、ユーザーも専用のモデルが必要です。
django.contrib.auth にはデフォルトのユーザーモデル User が組み込まれています。
そのため、この User モデルを利用するのであれば、特に自分でユーザーモデルを実装する必要はありません。
django-allauth でも django.contrib.auth が提供するデフォルト User を活用できます。
カスタムユーザーモデル作成を推奨
ただし、 Django 公式側としてはデフォルトの User モデルではなく、(たとえデフォルトのユーザーモデルで機能が十分であったとしても)カスタムユーザーモデルを作成することを強く推奨しています。
ここでいうカスタムユーザーとは、AbstractBaseUser もしくは AbstractUser というユーザーモデルの雛形を継承したモデルのことです。
デフォルトのユーザーの利用が推奨されない理由は、ユーザーというものが他のデータテーブルに依存することが多いです。
(例えばユーザーが外部キーとなっていたり、ManyToMany の関係を持っているような場合)
そのため、プロジェクトの途中からユーザーモデルそのものを差し替えようとすると、データベース上の不祥事が起こりやすくなるためです。
プロジェクトの初めからカスタムのユーザーを利用することでデータベース上でのトラブルを避けることができます。
カスタムのユーザーモデルを実装しよう!
それでは、カスタムのユーザーモデルを実装してみましょう!
まずカスタムユーザーモデルを記述するアプリケーションを作ります。
以下のコマンドを実行して accounts アプリケーションを作りましょう!
1python3 manage.py startapp accounts
アプリケーションを作成した後は、settings.py の INSTALLED_APPS にアプリを追加します。
1INSTALLED_APPS = [
2 'django.contrib.admin',
3 'django.contrib.auth',
4 'django.contrib.contenttypes',
5 'django.contrib.sessions',
6 'django.contrib.messages',
7 'django.contrib.staticfiles',
8 'diary',
9 'bootstrap4',
10 'widget_tweaks',
11 'accounts', # 追加
12]
では、いよいよカスタムユーザーを実装していきましょう。
カスタムユーザーの実装では「カスタムユーザーモデルに対応するクラス」と「ユーザー作成を担うメソッドを持ったクラス」の合計2つのクラスを実装する必要があります。
ユーザー作成を担うメソッドを持ったクラス
まず「ユーザー作成を担うメソッドを持ったクラス」を実装していきましょう。
accounts ディレクトリの models.py に以下のコードを記載しましょう。
1from django.contrib.auth.models import UserManager, AbstractUser
2
3class CustomUserManager(UserManager):
4 use_in_migrations = True
5
6 def _create_user(self, email, username, password, **extra_fields):
7 # create_user と create_superuser の共通処理
8 if not email:
9 raise ValueError('email must be set')
10 if not username:
11 raise ValueError('username must be set')
12
13 user = self.model(email=email, username=username, **extra_fields)
14 user.set_password(password)
15 user.save(using=self._db)
16
17 return user
18
19 def create_user(self, username, email=None, password=None, **extra_fields):
20
21 if not email:
22 raise ValueError('email must be set')
23 if not username:
24 raise ValueError('username must be set')
25
26 extra_fields.setdefault('is_staff', False)
27 extra_fields.setdefault('is_superuser', False)
28
29 return self._create_user(email, username, password, **extra_fields)
30
31 def create_superuser(self, username, email=None, password=None, **extra_fields):
32
33 extra_fields.setdefault('is_staff', True)
34 extra_fields.setdefault('is_superuser', True)
35
36 if extra_fields.get('is_staff') is not True:
37 raise ValueError('Superuser must have is_staff=True.')
38
39 if extra_fields.get('is_superuser') is not True:
40 raise ValueError('Superuser must have is_superuser=True.')
41
42 return self._create_user(email, username, password, **extra_fields)
create_user メソッドおよび create_superuser メソッドは、それぞれ一般ユーザーと管理者ユーザーを作成するメソッドです。
管理者ユーザーを作成する create_superuser メソッドでは、管理者を示すフラグとして is_staff 変数と is_superuser 変数に True を与えています。
カスタムユーザーモデルに対応するクラス
次に、この CustomUserManager の管理下におかれるカスタムユーザーモデルを作成します。
accounts ディレクトリの models.py に以下のコードを追記しましょう。
1class CustomUser(AbstractUser):
2 objects = CustomUserManager()
3
4 def __str__(self):
5 return self.email
CustomUser の実装が非常にシンプルである理由は、継承元の AbstractUser にあります。
django.contrib.auth に含まれている models.py に記載された、AbstractUser の実装コードの一部を以下に掲載します。
1class AbstractUser(AbstractBaseUser, PermissionsMixin):
2 """
3 An abstract base class implementing a fully featured User model with
4 admin-compliant permissions.
5
6 Username and password are required. Other fields are optional.
7 """
8 username_validator = UnicodeUsernameValidator()
9
10 # 一般的なユーザーに必要なメンバ変数
11 username = models.CharField(
12 _('username'),
13 max_length=150,
14 unique=True,
15 help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
16 validators=[username_validator],
17 error_messages={
18 'unique': _("A user with that username already exists."),
19 },
20 )
21 first_name = models.CharField(_('first name'), max_length=150, blank=True)
22 last_name = models.CharField(_('last name'), max_length=150, blank=True)
23 email = models.EmailField(_('email address'), blank=True)
24 is_staff = models.BooleanField(
25 _('staff status'),
26 default=False,
27 help_text=_('Designates whether the user can log into this admin site.'),
28 )
29 is_active = models.BooleanField(
30 _('active'),
31 default=True,
32 help_text=_(
33 'Designates whether this user should be treated as active. '
34 'Unselect this instead of deleting accounts.'
35 ),
36 )
37 date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
38 # 一般的なユーザーに必要なメンバ変数、ここまで
39 objects = UserManager()
40
41 EMAIL_FIELD = 'email'
42 USERNAME_FIELD = 'username'
43 REQUIRED_FIELDS = ['email']
44
45 〜省略〜
メンバ変数として username や email、is_staff(True が入ると管理サイトにアクセス可能)といったユーザーの状態を表す変数が最初から組み込まれています。
各変数を作成する上での引数(max_length の値など)を変えたい場合には、適宜変数をオーバーライドすればOKです。
最後に、ユーザーモデルとして CustomUser クラスを利用することを settings.py に明記します。
以下の1行を settings.py に追記しましょう!
(場所はどこでも構いません)
1AUTH_USER_MODEL = 'accounts.CustomUser'
以上の工程でカスタムユーザーの実装は完了です!
マイグレーション
作成したユーザーモデルのマイグレーションを実施しましょう!
下記のコマンドを実行して下さい。
1python3 manage.py makemigrations
2python3 manage.py migrate
makemigration の実行では下記のような表示が出てきます。
1Migrations for 'accounts':
2 accounts/migrations/0001_initial.py
3 - Create model CustomUser
では、これまでのコードの確認の意味を込めて、ユーザーを一つ作ってみましょう。
今回は allauth を利用したユーザー登録機能の実装ではなく、manage.py を通してスーパーユーザーを作ってみましょう!
以下のコマンドを実行して下さい。
1 python manage.py createsuperuser
ユーザー名、メールアドレス、パスワードの3つが尋ねられるので、適宜入力をして下さい。
メールアドレスはダミーのアドレス(例:test@example.com など)で構いません。
createsuperuser コマンドを通して作成したユーザーを確認してみます。
以下のコマンドを実行して下さい!
1 python manage.py dbshell
以下のように sqlite のプロンプトが表示されればOKです。
1SQLite version 3.24.0 2018-06-04 14:10:15
2Enter ".help" for usage hints.
3sqlite>
このプロンプトでselect * from accounts_customuser; というクエリを実行してみましょう。
以下のようにユーザー情報が確認できるはずです(パスワードは暗号化されています)。
11|pbkdf2_sha256$216000$VPPRXLlO2Dqt$A3UjVzvO/qjSE4Jccv4Q2oD2CBwA+PKM/3TZZVKebZM=||1|test|||test@example.com|1|1|2021-01-13 16:12:26.910746
ちなみにaccounts_customuserというテーブルは「アプリ名_モデルクラス名」という形式となっています。
最後に1点補足ですが、python の対話型シェルを通してもモデルデータを確認することができます。
下記のコマンドを実行してみましょう。
1python manage.py shell
シェルに対して下記の python コードを1行ずつ入力してみましょう。
1# 1つ目のコード
2from accounts.models import CustomUser
3# 2つ目のコード
4CustomUser.objects.all()
こちらの場合も以下のようにユーザーモデルの情報が得られます。
1<QuerySet [<CustomUser: test@example.com>]>
メールアドレスが表示されている理由は、CustomUser の実装において __str__ メソッドがメールアドレスを返すようにしてるためです。
ここまでのまとめ
Django における認証の概要とカスタムユーザーの作成のポイント以下です。
- Django のデフォルト認証機能を用いても基本的な機能は実装できるが、実装コストが多少かかるものがある
- django-allauth には一般的に必要とされる認証機能のビューやテンプレートなどが全て含まれてる
- Django ではカスタムユーザーの作成を強く推奨されている
- AbstractUser クラスを継承することで手軽にカスタムユーザーを作成可能
次回はいよいよ、 django-allauth を用いて、以下の機能を日記アプリに付けていきます!
- ログイン
- ログアウト
- ユーザー登録
では、早速やってみましょう!
allauth を使うための設定
まずは、 allauth アプリをプロジェクトに組み込みます。
config ディレクトリの settings.py を開いて下さい。
INSTALLED_APPS にアプリ全部で4つのアプリを追加します。
1INSTALLED_APPS = [
2 'django.contrib.admin',
3 'django.contrib.auth',
4 'django.contrib.contenttypes',
5 'django.contrib.sessions',
6 'django.contrib.messages',
7 'django.contrib.staticfiles',
8 'diary',
9 'bootstrap4',
10 'widget_tweaks',
11 'accounts',
12 'django.contrib.sites', # 追加
13 'allauth', # 追加
14 'allauth.account', # 追加
15 'allauth.socialaccount', # 追加
16]
また INSTALLED_APPS の下に、下記の一行を加えましょう。
1SITE_ID = 1
allauth パッケージは django.contrib.sites を利用したプロジェクトでなければ動かない仕組みになっています。
なお、 SITE_ID とは Django プロジェクトの識別値です。
allauth、allauth.account、allauth.socialaccount は allauth パッケージに含まれた Django アプリケーションです。
allauth.socialaccount は、主に gmail などのソーシャルアカウントによる認証機能に必要なものです。
ただ、allauth のテンプレートファイルの一部が allauth.socialaccount の内容を参照しているため、ソーシャルアカウント認証を使わない場合でもアプリ追加が必須となります。
認証の設定
アプリの追加が終わったら、次は認証の設定を行いましょう!
下記の内容を setttings.py に追加してください。
1AUTHENTICATION_BACKENDS = (
2 'django.contrib.auth.backends.ModelBackend', #デフォルトの認証基盤
3 'allauth.account.auth_backends.AuthenticationBackend' # メールアドレスとパスワードの両方を用いて認証するために必要
4)
5
6ACCOUNT_AUTHENTICATION_METHOD = 'email' # メールアドレス(とパスワードで)認証する
7ACCOUNT_USERNAME_REQUIRED = True # サインアップ(ユーザー登録)の時にユーザーネームを尋ねる
8ACCOUNT_EMAIL_REQUIRED = True # サインアップ(ユーザー登録)の時にメールアドレスを尋ねる
9ACCOUNT_EMAIL_VERIFICATION = 'mandatory' # メール検証を必須とする
10
11LOGIN_URL = '/account/login/' # ログインURLの設定
12LOGIN_REDIRECT_URL = '/index/' # ログイン後のリダイレクト先
13ACCOUNT_LOGOUT_REDIRECT_URL = '/account/login/' # ログアウト後のリダイレクト先
設定値の意味は、コードの隣に記載したコメントの通りです。
ACCOUNT_EMAIL_VERIFICATION は全部で3つの選択肢があります。
mandatory、option、そして none です。
mandatory は検証を必須とする、option はメール検証をしなくてもログインできる、none はメール検証をそもそも行ないません。
設定が完了したので、次は urls.py の設定をしてallauth アプリが持つビューを url と紐付けます。
config ディレクトリ内の urls.py を以下のように編集しましょう。
1from django.contrib import admin
2from django.urls import include, path
3
4urlpatterns = [
5 path('admin/', admin.site.urls),
6 path('', include('diary.urls')),
7 path('account/', include('allauth.urls')), # 追加
8]
ユーザー登録をしてみよう
これで設定完了です。
では、プロジェクトを起動してみましょう!
http://127.0.0.1:8000/account/login/ にアクセスしてみて下さい。
確認
以下のような画面が現れます!
allauth は、ビューだけでなくテンプレートも用意しているので、settings.py に設定を書き込み、urls.py の編集を行ったらすぐさま使えるようになります!
ユーザー登録のリンクをクリックしてみましょう!
ユーザー登録を実行
設定通り、メールアドレス、ユーザーネームそしてパスワードを尋ねられますので、全て入力してユーザー登録を実行してみて下さい。
すると connection refuse というエラー画面が出て来たのではないでしょうか?
これは、ACCOUNT_EMAIL_VERIFICATION = 'mandatory' としているにも関わらず、メールの配信の仕組みを設定していないからです。
一度 settings.py に戻り、下記の一行を追加してみて下さい。
1EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
これはメールをコンソール上に出力するための設定です。
通常のサービスでしたらメール配信用サーバーを立てる必要がありますが、それだけでもなかなか大変なので、今回はコンソール出力とします。
再度
では、もう一度ユーザー登録を実施してみましょう。
今度こそユーザー登録できる!
…と思ったかもしれませんが、残念ながら同じメールアドレスもしくは同じユーザーネームを設定した場合には、ユーザー登録が弾かれてしまったのではないでしょうか?
ここはひとまず、異なるメールアドレスとユーザーネームを与えて登録をし直してみて下さい。
今度こそユーザー登録が完了したのではないでしょうか?
先程の connection refuse のエラーでは、メール配信は失敗したものの、ユーザーオブジェクトの作成は完了していたということを意味していますので、メール配信周りの設定には注意をしましょう。
コンソール
コンソール画面を覗いてみて下さい。
以下のようなメールが届いているはずです。
「To Confirm this is correct」の行に url があります。
この url がメール検証用のものです!
確認
その url にアクセスすると以下のような画面が現れます。
「確認する」を押すと、メールの検証がなされてログインができるようになります!
ログイン後は設定に従い、index/ にリダイレクトします。
ログアウトリンクを貼ろう
先ほどはログインビューに対応する url に直にアクセスしましたが、ログアウトのリンクは私たちが作っているアプリ内部に貼りたいところです。
base.html の body タグ内を以下のように編集して、ログアウトリンクを付けましょう!
1<body>
2<a href="{% url 'account_logout' %}">ログアウト</a> <!-- 追加 -->
3{% block content %}
4{% endblock %}
5</body>
account_logout が allauth のログアウトビューへの逆引き名となっています。
確認
ログイン後、トップページが以下のようになります。
【最終回】へつづく!
今回は allauth を使って、ユーザー登録やログイン・ログアウトなど認証系の機能を実装しました。
今回の記事のポイント以下の点です。
- allauth を使えば認証系機能はビューもテンプレートもすぐに利用できる
- メールのコンソール出力は設定ファイルに一行追加するだけ
- テンプレートにログアウトなどのリンクを貼りたい場合は、いつも通り url タグにビューの逆引き名を記載する
次回はいよいよ最終回です。
これまでの内容のまとめと、便利なライブラリの紹介を行っていきます!
最終回はこちら!
2021.09.06【最終回】Djangoで日記アプリを作ろう~総復習編~Djangoで日記アプリを作ろう ~総復習編~前回は Django の便利な認証系パッケージ allauth を使って...
こちらの記事もオススメ!
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
2020.07.30Python 特集実装編※最新記事順Responder + Firestore でモダンかつサーバーレスなブログシステムを作ってみた!P...
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit