本記事では「初めてのDjango(発展編)」として、初めてDjangoというフレームワークを使用してWebアプリケーションを作ってみたい!という方向けに、簡単なアプリケーションを作成しながら基本的な知識をご紹介していきます。
本シリーズの全容はこのようになっております。
- (入門編 #01)単一ページのアプリケーション作成まで
- (入門編 #02)CSSの適用からベーステンプレートの作成まで
- (入門編 #03)コンテキスト情報とテンプレートタグの活用
- (入門編 #04)お問い合わせフォームの設置、メール送信処理まで
- (入門編 #05)お問い合わせ送信後にメッセージを表示させる
- (入門編 #06)アプリケーションをGAEにデプロイ
- (発展編 #01)データベースの設定を行う
- (発展編 #02)カスタムユーザーモデルの定義、マイグレーション
- (発展編 #03)django-allauthで認証機能を実装する ←イマココ
- (発展編 #04)ListViewを用いてモデル情報を描写する
- (発展編 #05)CreateViewを使用してデータベースを更新
- (発展編 #06)DetailViewを用いて詳細表示
- (発展編 #07)UpdateViewを用いてデータベース編集
- (発展編 #08)DeleteViewを用いてデータベースのレコード削除
・djangoの認証機能の種類について
・django-allauthを用いた認証機能の実装
django-allauthとは
Djangoでユーザー認証機能(ログイン機能)を実装する場合、デフォルトで用意されている django.contrib.auth を使用して実現することができます。
しかし今回は django-allauth というDjango用のパッケージを使用して認証機能を実装していきます。メリットとして、
- 簡単に実装できる
- ソーシャル認証機能までサポートされている
が挙げられます。ソーシャル認証機能とは、TwitterやFacebook、GitHubなどのアカウントを使用してログインできる機能のことです。サポートされている会社は20社以上に及びます。
その他さまざまな機能があるのにも関わらず、無料で使用することができます。
認証機能の実装
ここから実際に、django-allauthを使用して認証機能を実装していきます。
パッケージのインストール
pipコマンドで django-allauth をインストールします。
仮想環境に入っていることを確認しましょう。
(venv_cafe)$ pip install django-allauth
プロジェクト設定ファイルの編集
django-allauth をDjangoで使用できるために settings.py を下記のように編集する必要があります
# ... 略 ...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'cafe_app.apps.CafeAppConfig',
'accounts.apps.AccountsConfig',
'django.contrib.sites', # 追加
'allauth', # 追加
'allauth.account', # 追加
'allauth.socialaccount', # 追加
]
# ... 略 ...
# django.contrib.sites用のサイト識別IDを設定
SITE_ID = 1
# 認証バックエンドの設定
AUTHENTICATION_BACKENDS = (
'allauth.account.auth_backends.AuthenticationBackend', # 一般ユーザー用(メールアドレス認証)
'django.contrib.auth.backends.ModelBackend', # 管理サイト用(ユーザー名認証)
)
# 認証方式を「メールアドレス」に設定
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_USERNAME_REQUIRED = False # ユーザー名は使用しない
# サインアップにメールアドレス確認を挟むように設定
ACCOUNT_EMAIL_VERIFICATION = 'mandatory'
ACCOUNT_EMAIL_REQUIRED = True
# ログイン / ログアウト後のリダイレクト先を設定
LOGIN_REDIRECT_URL = 'cafe_app:index'
ACCOUNT_LOGOUT_REDIRECT_URL = 'account_login'
# 「ログアウト」を一回クリックしただけでログアウトできるように設定
ACCOUNT_LOGOUT_ON_GET = True
# django-allauthが送信するメールの件名に自動付与される接頭辞を空にする設定
ACCOUNT_EMAIL_SUBJECT_PREFIX = ''
# デフォルトのメール送信元の設定
DEFAULT_FROM_EMAIL = '<任意の送信元>'
補足
- 認証バックエンド
-
djangoの認証は、認証用のバックエンド(認証をテストするクラス)によって行われます。AUTHENTICATION_BACKENDSに配列として渡すことでバックエンドを追加することができる。認証時には追加した順に認証を行い、もし成功すれば認証したユーザーを返す。
デフォルトは django.contrib.auth.backends.ModelBackend のみ。
- サインアップにメールアドレス確認をはさむ
-
上記のように記述することで、
- ユーザー登録(仮登録)
- メールが送信される
- 送信されたメールのリンクをクリック
- ユーザー登録(本登録)
という流れにすることができます。メールアドレスの認証をはさむことで本人確認を行うことができます。
- ログアウト後の挙動
-
django-allauthのデフォルトの設定では、「ログアウト」ボタンを押した後に
- ログアウト画面が表示される
- 「ログアウト」をクリック
- ログアウト
というくどい流れになっております。「ログアウト」を一回クリックしただけですぐにログアウトできるように、「ACCOUNT_LOGOUT_ON_GET = True」と記述しています。
ルーティングの設定
プロジェクト側の urls.py を下記のように編集します。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('cafe_app.urls')),
path('accounts/', include('allauth.urls')),
]
このように記述することで、https://<ホスト名>/accounts/~ というURLにアクセスした際に、django-allauthがデフォルトで持っている urls.py に処理をお願いしています。
django-allauthの持つデフォルトのルーティングは下記のようになります。
URL | 処理内容 |
/accounts/signup/ | サインアップ(エントリー画面表示) |
/accounts/confirm-email/ | サインアップ(メール送信) |
/accounts/confirm-email/<キー>/ | サインアップ(確定) |
/accounts/login/ | ログイン |
/accounts/logout/ | ログアウト |
/accounts/password/reset/ | パスワードリセット(エントリー画面表示) |
/accounts/password/reset/done/ | パスワードリセット(メール送信) |
/accounts/password/reset/key/<キー>-set-password/ | パスワードリセット(パスワード設定) |
/accounts/password/reset/key/done/ | パスワードリセット(確定) |
django-allauthのテンプレートを上書きする
通常、新規ページを作成したい場合は、各アプリケーションの templates/ ディレクトリにHTMLファイルを作成していました。
しかしdjango-allauthにはデフォルトのテンプレートが備わっております(allauth/templates/accountにあります)。なので独自に新しくテンプレートを作成しなくても動作はするのですが、今回は上書きすることで独自のテンプレートを作成します。
ちなみにデフォルトのサインアップ画面はこんな感じです。
「accounts/templates/account」ディレクトリを新規作成し、その配下に上書きしたいデフォルトテンプレートと同名のファイルを作成することで、独自のテンプレートを作成することができます。
「accounts/templates/account/」ディレクトリに下記の8つのHTMLファイルを作成し、それぞれ下記の内容をコピペしてください。
多いですが頑張りましょう、、、。
※参考までに完成後の画像を載せておりますが、後述するCSSの内容を加えた後のイメージです。
サインアップ(エントリー画面)
{% extends 'base.html' %}
{% load static %}
{% block title %}サインアップ{% endblock title %}
{% block head %}<link type="text/css" rel="stylesheet" href="{% static 'css/accounts-style.css' %}">{% endblock head %}
{% block contents %}
<section class="section-accounts" id="section-signup">
<div class="wrapperHeader">
<h2>ユーザー登録</h2>
<p>すでにアカウントをお持ちであれば、こちらから<a href="{% url 'account_login' %}"><span class="accounts-link">ログイン</span></a>してください。</p>
</div>
<div class="wrapper-accounts-form">
<form method="post" action="{% url 'account_signup' %}" class="form-accounts">
{% csrf_token %}
<div class="form-content">
<label>Email</label>
<input type="email" name="email" autocomplete="email" required id="id_email">
</div>
<div class="form-content">
<label>Password</label>
<input type="password" name="password1" autocomplete="new-password" required id="id_password1">
</div>
<div class="form-content">
<label>Password(確認用)</label>
<input type="password" name="password2" autocomplete="new-password" required id="id_password2">
</div>
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}">
{% endif %}
<button class="accounts-btn" type="submit">登録</button>
</form>
</div>
<a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}
サインアップ(メール送信)
{% extends 'base.html' %}
{% load static %}
{% block title %}メールアドレスの確認{% endblock title %}
{% block head %}<link type="text/css" rel="stylesheet" href="{% static 'css/accounts-style.css' %}">{% endblock head %}
{% block contents %}
<section class="section-accounts" id="section-signup">
<div class="wrapperHeader">
<h2>メールアドレスを確認してください</h2>
<p>確認のメールを送信しました。</p>
<p>
メールに記載されたリンクをクリックして、ユーザー登録を完了させてください。<br>
数分経っても確認のメールが届かない場合はご連絡ください。
</p>
</div>
<a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}
サインアップ(確定)
{% extends 'base.html' %}
{% load static %}
{% block title %}サインアップの確定{% endblock title %}
{% block head %}<link type="text/css" rel="stylesheet" href="{% static 'css/accounts-style.css' %}">{% endblock head %}
{% block contents %}
<section class="section-accounts" id="section-signup">
<div class="wrapperHeader">
<h2>ユーザー登録</h2>
</div>
{% if confirmation %}
<div class="wrapper-accounts-form text-align-center">
<p class="notion">ユーザー登録を確定するには、以下のボタンを押してください。</p>
<form method="post" action="{% url 'account_confirm_email' confirmation.key %}" class="form-accounts">
{% csrf_token %}
<button class="accounts-btn" type="submit">確定</button>
</form>
</div>
{% else %}
{% url 'account_email' as email_url %}
<div class="text-align-center">
<p>リンクの有効期限が過ぎています。<a href="{{ email_url }}">再申請</a>.</p>
</div>
{% endif %}
<a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}
ログイン画面
{% extends 'base.html' %}
{% load static %}
{% block title %}ログイン{% endblock title %}
{% block head %}<link type="text/css" rel="stylesheet" href="{% static 'css/accounts-style.css' %}">{% endblock head %}
{% block contents %}
<section class="section-accounts" id="section-signup">
<div class="wrapperHeader">
<h2>ログイン</h2>
</div>
<div class="wrapper-accounts-form">
<form method="post" action="{% url 'account_login' %}" class="form-accounts">
{% csrf_token %}
<div class="form-content">
<label>Email</label>
<input type="email" name="login" autocomplete="email" required id="id_login">
</div>
<div class="form-content">
<label>Password</label>
<input type="password" name="password" autocomplete="current-password" required id="id_password">
</div>
<div class="radio-btn">
<input type="checkbox" name="remember" id="id_remember">
<label>ログイン状態を保持する</label>
</div>
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}">
{% endif %}
<button class="accounts-btn" type="submit">ログイン</button>
<p class="notion"><a href="{% url 'account_reset_password' %}">パスワードをお忘れですか?</a></p>
</form>
</div>
<a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}
パスワードリセット(エントリー)
{% extends 'base.html' %}
{% load static %}
{% block title %}パスワードリセット{% endblock title %}
{% block head %}<link type="text/css" rel="stylesheet" href="{% static 'css/accounts-style.css' %}">{% endblock head %}
{% block contents %}
<section class="section-accounts" id="section-signup">
<div class="wrapperHeader">
<h2>パスワードリセット</h2>
{% if user.is_authenticated %}
{% include "account/snippets/already_logged_in.html" %}
{% endif %}
<p>パスワードリセット用のメールを送信します。</p>
</div>
<div class="wrapper-accounts-form">
<form method="post" action="{% url 'account_reset_password' %}" class="form-accounts">
{% csrf_token %}
<div class="form-content">
<label>Email</label>
<input type="email" name="email" placeholder="メールアドレス" autocomplete="email" required id="id_email">
</div>
<button class="accounts-btn" type="submit">送信</button>
</form>
</div>
<a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}
パスワードリセット(メール送信)
{% extends 'base.html' %}
{% load static %}
{% block title %}パスワードリセット完了{% endblock title %}
{% block head %}<link type="text/css" rel="stylesheet" href="{% static 'css/accounts-style.css' %}">{% endblock head %}
{% block contents %}
<section class="section-accounts" id="section-signup">
<div class="wrapperHeader">
<h2>パスワードリセット</h2>
{% if user.is_authenticated %}
{% include "account/snippets/already_logged_in.html" %}
{% endif %}
<p>パスワードリセット用のメールを送信しました。</p>
</div>
<a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}
パスワードリセット(パスワード設定)
{% extends 'base.html' %}
{% load static %}
{% block title %}パスワードリセット{% endblock title %}
{% block head %}<link type="text/css" rel="stylesheet" href="{% static 'css/accounts-style.css' %}">{% endblock head %}
{% block contents %}
<section class="section-accounts" id="section-signup">
<div class="wrapperHeader">
<h2>{% if token_fail %}不正トークン{% else %}パスワードリセット{% endif %}</h2>
<p>アカウントをまだお持ちでなければ、こちらから<a href="{% url 'account_signup' %}"><span class="accounts-link">ユーザー登録</span></a>してください。</p>
</div>
{% if token_fail %}
{% url 'account_reset_password' as passwd_reset_url %}
{% else %}
{% if form %}
<div class="wrapper-accounts-form">
<form method="post" action="{{ passwd_reset_url }}" class="form-accounts">
{% csrf_token %}
<div class="form-content">
<input type="password1" name="password1" placeholder="新しいパスワード" autocomplete="new-password" required id="id_password1">
</div>
<div class="form-content">
<input type="password2" name="password2" placeholder="新しいパスワード(再入力)" autocomplete="new-password" required id="id_password2">
</div>
<button class="accounts-btn" type="submit">変更</button>
</form>
</div>
{% else %}
<div class="text-align-center">
<p class="notion">パスワードは変更されています。</p>
</div>
{% endif %}
{% endif %}
<a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}
パスワードリセット(確定)
{% extends 'base.html' %}
{% load static %}
{% block title %}パスワードリセット変更完了{% endblock title %}
{% block head %}<link type="text/css" rel="stylesheet" href="{% static 'css/accounts-style.css' %}">{% endblock head %}
{% block contents %}
<section class="section-accounts" id="section-signup">
<div class="wrapperHeader">
<h2>パスワードリセット完了</h2>
<p>パスワードが変更されました。</p>
</div>
<a class="backToHome" href="{% url 'cafe_app:index' %}"><<<ホームに戻る</a>
</section>
{% endblock contents %}
ついでに静的ファイルを設置するディレクトリ(「static/css/」)に新規ファイル accounts-style.css を作成し、下記のように編集しましょう。
accounts-style.css
/* 共通 */
:root {
--dark-color: #442f1e;
--semi-dark-color: #614039;
--base-color: #d3c2bb;
--accent-color: #598493;
--footer-height: 100px;
}
* {
margin: 0;
padding: 0;
list-style: none;
font-family: -apple-system, Calibri、Candara、Segoe、Segoe UI、Optima、Arial、sans-serif;
box-sizing: border-box;
}
a:link, a:visited, a:hover, a:active {
color: inherit;
text-decoration: none;
}
.text-align-center {
text-align: center;
}
.notion {
display: block;
margin: 20px;
}
.notion a {
border-bottom: solid 1px var(--dark-color);
}
.section-accounts {
background-color: var(--base-color);
min-height: calc(100vh - var(--footer-height));
}
.wrapperHeader {
text-align: center;
padding: 3rem;
}
.wrapperHeader p {
display: block;
margin-top: 2rem;
}
.accounts-link {
font-weight: bold;
}
.form-accounts {
width: fit-content;
margin: 0 auto;
}
.form-content {
width: 100%;
display: flex;
flex-direction: row;
justify-content: right;
align-items: center;
margin: 20px auto;
}
.form-content input {
background-color: inherit;
width: 450px;
height: 24px;
font-size: 16px;
border: none;
border-bottom: solid var(--dark-color) 1px;
margin-left: 10px;
}
.form-content input:focus {
outline: none;
border-bottom: solid var(--accent-color) 2px;
}
.form-content label:after {
position: relative;
content: ' : ';
}
.accounts-btn {
background-color: var(--accent-color);
display: block;
padding: 10px 70px;
border: solid var(--semi-dark-color) 1px;
font-size: 20px;
margin: 3rem auto 0 auto;
box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.24) 0px 1px 2px;
transition-duration: .2s;
}
.accounts-btn:hover {
transform: translateY(-2px);
opacity: .9;
cursor: pointer;
}
.radio-btn {
width: fit-content;
margin: 0 auto;
}
django-allauthのメールの内容を変更する
サインアップやパスワードのリセット時などに送信されるメールの内容を修正します。
「accounts/templates/account/email/」ディレクトリを作成し、下記の4つのファイルを新規作成しましょう!さらに下記の内容をそれぞれコピペしてください(文言は変えていただいて構いません)。
email_confirmation_message.txt
ご登録ありがとうございます。
登録を続けるには、以下のリンクをクリックしてください。
{{ activate_url }}
サインアップ時のメールアドレス確認メールの本文です。
email_confirmation_subject.txt
ユーザー登録確認メール
サインアップ時のメールアドレス確認メールの件名です。
password_reset_key_message.txt
パスワードリセットが申請されました。処理を続けるには、以下のリンクをクリックしてください。
{{ password_reset_url }}
パスワードリセット時のメールの本文です。
password_reset_key_subject.txt
パスワードリセット
パスワードリセット時のメールの件名です。
認証ページへのリンクを貼る
サインアップ / ログインなど、認証ページへのリンクをトップページに貼り付けましょう。cafe_app内の index.html を下記のように編集します。
{% block contents %}
<div class="topPage">
<img class="topMainImg" src="{% static 'assets/img/first-view.jpg' %}" alt="first-view">
<div class="wrapperNav">
<nav>
<ul>
<li><a href="#">HOME</a></li>
<li><a href="{% url 'cafe_app:menu' %}">MENU</a></li>
{% if user.is_authenticated %}
<li><a href="{% url 'account_logout' %}">LOG OUT</a></li>
{% else %}
<li><a href="{% url 'account_signup' %}">SIGN UP</a></li>
<li><a href="{% url 'account_login' %}">LOG IN</a></li>
{% endif %}
<li><a href="{% url 'cafe_app:contact' %}">CONTACT</a></li>
</ul>
</nav>
</div>
</div>
<section class="welcome">
<div>
<h2>ようこそ、hoge cafeへ</h2>
<p>こちらはhoge cafeの公式サイトです。</p>
</div>
</section>
{% endblock contents %}
{% if user.is_authenticated %} によって、ユーザーがログインしているか否かで処理を分けるています。
{% if user.is_authenticated %}
... ログインしている場合の内容 ...
{% else %}
... ログインしていない場合の内容 ...
{% endif %}
ログインしている場合はログアウトのみ、ログインしていない場合はログイン / サインアップを表示させています。
ログインしているユーザー名を表示させる
ログイン後にテンプレート側でユーザー名を取得し、表示させましょう。
なくてもいいのですが、Webアプリぽい感じをだしましょう。
index.htmlに一部追加します。
{% load account %}
<!-- ... 略 ... -->
{% block contents %}
<!-- ... 略 ... -->
<section class="welcome">
<div>
<h2>ようこそ、hoge cafeへ</h2>
<p>こちらはhoge cafeの公式サイトです。</p>
<p>{% user_display user %}</p>
</div>
</section>
{% endblock contents %}
{% load account %} と記述してから {% user_display user %} とすることでログインしているユーザー名を表示することができます。
マイグレーション
ターミナルで下記のコマンドによりマイグレーションを行います。
(venv_cafe)$ python manage.py makemigrations
(venv_cafe)$ python manage.py migrate
マイグレーションについてはこちらで解説しております。
マイグレーションが完了すれば、無事認証機能の実装が完了です!お疲れ様でした!
この時点でのコード全体をGithubに載せております。確認用としてお使いいただければと思います。
まとめ
本セクションでは、django-allauthを使用してDjangoアプリケーションに認証機能を実装する方法をご紹介させていただきました。
次回からは、本記事で実装した認証機能を活かして、ログインしたユーザーのみが利用できる会員限定機能についてご紹介していきます。
ご覧いただきありがとうございました!
コメント
コメント一覧 (4件)
[…] (発展編 #03)django-allauthで認証機能を実装する […]
[…] (発展編 #03)django-allauthで認証機能を実装する […]
[…] (発展編 #03)django-allauthで認証機能を実装する […]
[…] 【初めてのDjango】(発展編 #03)django-allauthで認証機能を実装する […]