このページでは、Django におけるログイン機能について解説していきます。
といっても、実はこのサイトでは下記ページで既にログイン機能の実現方法については解説しています。

ですので、このページではログイン機能やログイン機能の実現方法自体については解説せず、ここまでの Django 入門の中で開発してきた掲示板アプリに対してログイン機能を搭載する手順を説明していきたいと思います。
ログイン機能自体についてや、ログイン機能を実現する上で必要になる実装の全体像・それらの実装が必要になろ理由等については上記ページを読んでいただいた方が分かりやすいと思いますので、これらについても詳しく知りたい方は是非事前に上記ページにも目を通してみてください。
アプリへのログイン機能の搭載
では、まずは掲示板アプリにログイン機能を搭載することで出来るようになることと、ログイン機能を搭載する上で必要になる実装について解説していきます。
この Django 入門に関しては連載形式となっており、ここでは前回下記ページの 掲示板アプリでモデルフォームを利用してみる で作成したウェブアプリに対してログイン機能を導入する形で、ログイン機能の実装例を示していきたいと思います。

ログイン機能の搭載により出来るようになること
これまでの掲示板アプリはログイン機能がなかったため、誰でも利用可能なウェブアプリとなっていました。
それに対し、ログイン機能を搭載することにより、掲示板アプリはログインしたユーザーのみが利用可能となります。
より具体的には、未ログインユーザーがウェブアプリを利用しようとした際には強制的にログインページにリダイレクトされるようになり、未ログインユーザーはログインを行わないと掲示板アプリを利用できないようになります。
また、ログイン機能が搭載されることにより、ログイン中のユーザー、つまりウェブアプリを利用しているユーザーを特定できるようになります。
例えば、これまでは掲示板にコメントを投稿する際には、ユーザーが逐一手動で投稿者を自分自身に選択する必要がありました。
ですが、ログイン機能を搭載すれば、ログイン中のユーザーが特定できるようになるため、ウェブアプリ側で投稿者をログインユーザーに設定することが可能となります。したがって、ユーザーが手動で自身を投稿者として設定する必要がなくなります。要は、ユーザーから見れば、投稿者が自動的に設定されることになります。
このように、ログイン機能を利用することで、未ログインユーザーのウェブアプリの利用を制限したり、ログインユーザーをユーザーの操作と関連づけて処理を行うようなことができるようになります。
スポンサーリンク
ログイン機能を搭載するために必要な実装
ただ、こういったログイン機能を実現するためには、当然そのための実装が必要となります。今回扱う掲示板アプリでは下記のような実装を行ってログイン機能を実現していくことになります。
- ログイン管理用のモデルの追加
- モデルの定義
- 認証用モデルの設定
- ログインの実現
- フォームの追加 / 変更
- ログインフォーム
- ユーザー登録フォーム
- ビューの追加 / 変更
- ログインビュー
- ログアウトビュー
- ユーザー登録ビュー
- テンプレートの追加 / 変更
- ログインページ
- ナビゲーションバーへのリンクの追加
- フォームの追加 / 変更
- 未ログインユーザーのアクセス制限
- ビューの変更
- リダイレクト先の設定
- ログインユーザーとコメントの関連付け
- コメント投稿フォームの変更
- コメントとユーザーの関連付け
ここからは、上記の各項目について具体的な実装を示しながら説明していきたいと思います。
ログイン管理用のモデルの追加
では、掲示板アプリにログイン機能を追加していきましょう!
まずは、ログイン管理用のモデルクラスの追加を行なっていきます。
モデルの定義の変更
これまで、掲示板アプリでは下記のように models.py
でモデルクラスの定義を行なっていました。
from django.db import models
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator
min_age = 0
max_age = 200
def check_username(username):
if not username.isalnum() or not username.isascii():
raise ValidationError(_('usernameにはアルファベットと数字のみ入力可能です'))
if not username[0].isalpha():
raise ValidationError(_('usernameの最初の文字はアルファベットにしてください'))
class User(models.Model):
username = models.CharField(max_length=32, validators=[check_username])
email = models.EmailField()
age = models.IntegerField(validators=[MinValueValidator(min_age), MaxValueValidator(max_age)])
def __str__(self):
return self.username
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='comments')
text = models.CharField(max_length=256)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.text[:10]
今回変更するのは User
モデルクラスとなります。この User
モデルクラスはユーザーの管理を行うクラスとなっています。
この User
モデルクラスをそのまま利用してログインを実現してもいいのですが、特にログインなどの認証が必要な機能を実現する際にはパスワードの扱いに気をつける必要があります。このパスワードが平文でそのままデータベースに保存されているとウェブアプリ利用者のパスワード流出のリスクが高くなります。
こういったセキュリティ面のことを考えると、自身でモデルクラスを定義してセキュリティ対策を行うよりも、元々セキュリティ対策が実施されているモデルクラスを利用する方が無難です。
そのため、上記の User
モデルクラスを、models.Model
を継承するのではなく、AbstractUser
を継承する形で定義するように変更します。具体的には、models.py
を下記のように変更します。ログインを実現する上での models.py
の変更はこれで完了となります。
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from django.contrib.auth.models import AbstractUser
max_age = 200
min_age = 0
class CustomUser(AbstractUser):
age = models.IntegerField(validators=[MinValueValidator(min_age), MaxValueValidator(max_age)])
class Comment(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True, related_name='comments')
text = models.CharField(max_length=256)
date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.text[:10]
CustomUser
が、従来の User
と同様にユーザーを管理するモデルクラスとなります。
この AbstractUser
は Django
フレームワークに用意されているアプリ auth
が提供するモデルクラスです。この AbstractUser
には認証を実現する上で必要となるフィールドや機能が用意されており、このモデルクラスを継承することで、サブクラスとなるモデルクラスでも認証を簡単に実現できるようになります。
また、AbstractUser
には username
フィールド、email
フィールドが既に定義されているため、CustomUser
からはこれらのフィールドの定義は行わないようにしています。ただし、age
フィールドは AbstractUser
では定義されていないため、このフィールドに関してのみ CustomUser
モデルクラスで定義するようにしています。
username
フィールドの妥当性の検証を行う関数として check_username
を用意していましたが、この関数も不要となります
また、AbstractUser
には password
というフィールドが用意されており、このフィールドでパスワード管理されることになります。そして、このフィールドで管理されるパスワードは、RegisterForm の定義 で紹介する UserCreationForm
を利用することによりユーザー登録時に自動的に暗号化(ハッシュ化)されることになります。したがって、安全に認証機能等を実現することができることになります。
スポンサーリンク
認証モデルの設定
ということで、ログイン時には先ほど定義した CustomUser
での認証を行なっていくことになります。
ただし、Django で開発するウェブアプリでは、そのウェブアプリが認証を行うか否かに関わらず、認証に利用するモデルクラスとして auth
が提供する User
モデルクラスがデフォルトで設定されています。つまり、上記のように models.py
に CustomUser
を定義しただけでは認証に CustomUser
は利用されません。
そのため、認証に利用するモデルを CustomUser
に設定するための手順が別途必要になります。
ただ、この手順は非常に簡単で、settings.py
に下記の1行を追加するだけになります。settings.py
の最後の行に追記するので良いです。
AUTH_USER_MODEL = 'forum.CustomUser'
AUTH_USER_MODEL
が認証モデルを設定する変数となっています。そして、この変数に 'forum.CustomUser'
を設定することで、認証時には forum
アプリの models.py
に定義した CustomUser
を利用することを宣言することができます。
そして、これによりログインフォームでログインを行う際には CustomUser
モデルクラスの username
フィールドと password
フィールドの値に基づいた認証が行われるようになります。
もう少し具体的に言えば、CustomUser
モデルクラスに対応するテーブルのレコードの中に、ログインフォームから送信された usename
フィールドと同じ、かつ、ログインフォームから送信された password
フィールドと同じものを持つレコードが存在する場合、認証 OK であると判断されるようになります。
で、このような認証を実現するためにはログインフォームを定義する必要があります。また、そのログインフォームを表示するビューやテンプレートも必要となります。
ログインの実現
ということで、次はログインフォームの定義やログインフォームを表示するビューの定義を行なっていくことでログインを実現していきたいと思います。
また、ログインを実現する上ではユーザー登録も必要ですし、さらにログイン状態を解除するログアウトも必要となりますので、これらの「ログイン」「ユーザー登録」「ログアウト」の3つを同時に実現していきたいと思います。
特にログインとログアウトは、これまで開発してきた掲示板ウェブアプリにおいては新機能となるため、実装の新規追加が必要となります。また、ユーザー登録に関しては既に機能を実装しているため、それを変更していくことになります。
フォームの追加 / 変更
まず、フォームを forms.py
に定義していきます。
ここでは、ログイン用のフォームを定義し、さらにユーザー登録用のフォームの変更を行なっていきます。ログアウト用のフォームは不要です。
変更前の forms.py
念の為載せておくと、現状の forms.py
は下記のようになっています。
from django import forms
from .models import User, Comment, max_age, min_age
class RegisterForm(forms.ModelForm):
class Meta:
model = User
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['age'].widget.attrs.update({
'min': min_age,
'max': max_age,
})
class PostForm(forms.ModelForm):
class Meta:
model = Comment
exclude = ['date']
widgets = {
'text': forms.Textarea
}
LoginForm
の定義
まず、ログイン用のフォームとして LoginForm
を定義していきます。ログイン用のフォームは auth
から提供される AuthenticationForm
というクラスのサブクラスとして定義することで簡単に実現することができます。
実際の LoginForm
の定義は下記のようになります。この章では追加部分、変更部分のソースコードを紹介していくことになりますが、最終的な forms.py
に関しても後述の ログインユーザーとコメントの関連付け で紹介を行います。
from django.contrib.auth.forms import AuthenticationForm
class LoginForm(AuthenticationForm):
pass
この LoginForm
は単に AuthenticationForm
を継承しているだけですが、AuthenticationForm
が username
フィールドと password
フィールドを持っているため、LoginForm
が表示されると username
フィールドと password
フィールドが表示されることになります。したがって、認証に必要な情報がユーザーから入力可能なフォームが表示されることになります。
また、通常のフォームクラスの場合は is_valid
実行時にはデータの妥当性が検証されるだけですが、 AuthenticationForm
のサブクラスとして定義してやることで is_valid
実行時に認証まで行われるようになります。
したがって、LoginForm
のフォームからデータが送信されてきた際に is_valid
メソッドを実行するようにし、さらに is_valid
メソッドが True
の場合のみログイン処理を行うようにしてやれば、登録済みのユーザーの場合のみログイン可能なフォームを実現できることになります。この辺りの処理はビュー側で実装していきます。
RegisterForm
の定義
続いて、ユーザー登録フォームである RegisterForm
を変更していきます。
この RegisterForm
は元々 forms.ModelForm
のサブクラスとして定義していましたが、UserCreationForm
のサブクラスとして定義するように変更します。
from .models import max_age, min_age
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import get_user_model
User = get_user_model()
class RegisterForm(UserCreationForm):
class Meta:
model = User
fields = ['username', 'email', 'age']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['age'].widget.attrs.update({
'max': max_age,
'min': min_age,
})
ポイントを3点ほど説明しておきます。
ポイントの1つ目は、パスワードの暗号化になります。UserCreationForm
のサブクラスとしてフォームクラスを定義することで、フォーム表示時に「パスワード入力フィールド」と「確認用パスワード入力フィールド」が表示されるようになり、さらにフォームから送信されてきたデータをデータベースに保存する際にパスワードがハッシュ値に暗号化されるようになります。
forms.ModelForm
のサブクラスの場合、パスワードを別途暗号化するような処理を実装しないとパスワードが平文で保存されることになってしまうため注意してください。
ポイントの2つ目は、get_user_model
関数の利用になります。get_user_model
は、settings.py
の AUTH_USER_MODEL
に指定したモデルクラスを取得する関数になります。認証モデルの設定 で AUTH_USER_MODEL
に 'forum.CustomUser'
を指定しているため、get_user_model
関数を実行すれば返却値として forum
アプリの models.py
に定義された CustomUser
が取得できることになります。
そのため、結果的に RegisterForm
の定義における model = User
では右辺で CustomUser
を指定していることになり、CustomUser
に基づいたモデルフォームクラスとして RegisterForm
が定義されることになります。
ポイントの3つ目は、表示するフィールドの項目になります。元々 fields
には '__all__'
を指定しており、モデルの持つ全フィールドをフォームに表示するようになっていました。変更前に model
に指定していた User
は 'username'
・ 'email'
・'age'
の3つのフィールドしか持っていなかったので全フィールドを表示するので問題なかったのですが、変更後に model
に指定されるようになった CustomUser
は AbstractUser
を継承しているため、ユーザー管理用の様々なフィールドが追加されており、全フィールドを表示すると余計なフィールドも含めて大量のフィールドが表示されることになります。
そのため、fields = ['username', 'email', 'age']
を指定するように変更し、表示されるフィールドを必要なものだけに絞るようにしています。
スポンサーリンク
ビューの追加 / 変更
続いて、ログインを実現するためのビューの追加 / 変更を views.py
に対して行なっていきます。
ログイン用のビュー login_view
の追加
まず、ログイン用のビューの関数 login_view
を追加していきます。
追加する login_view
は下記のような関数になります。この章で紹介するソースコードではポイントとなる import
以外は省略しているので注意してください。後述の コメントとユーザーの関連付け で最終的に出来上がった views.py
でまとめて紹介したいと思います。
from django.contrib.auth import login
def login_view(request):
if request.method == 'POST':
form = LoginForm(request, data=request.POST)
if form.is_valid():
user = form.get_user()
if user:
login(request, user)
return redirect('user', user.id)
else:
form = LoginForm()
context = {
'form': form,
}
return render(request, 'forum/login.html', context)
この login_view
は、基本的には他のフォームを扱うビューの関数と同様の作りになっています。
すなわち、request.method
が 'POST'
の場合は、送信されて来たデータからフォームクラスのインスタンスを生成し、is_valid
メソッドの実行結果が True
である場合に、すなわち送信されて来たデータが妥当であると判断された場合に、その「フォームに応じた処理」を実施するようになっています。
また、request.method
が 'POST'
以外の場合は単にフォームクラスのインスタンスをテンプレートにコンテキストとして渡してフォームの表示を行なっているだけです。
この辺りは他のフォームを扱うビューと同じ作りになるのですが、ログインを実現するためには「フォームに応じた処理」としてログイン処理を実現する必要があります。ここがポイントになります。
そのため、login_view
では、is_valid
が True
を返却した際には login
関数を実行するようにしています。この login
関数こそがユーザーログインを実現する関数となります。そして、is_valid
が True
を返却したということは認証が OK であると判断されたことになるため、認証 OK の場合のみ login
関数でのユーザーログインが行われることになります。
この login
関数の引数には「リクエスト」と「ログインしようとしているユーザー」の2つを指定する必要があります。ビューの関数は「リクエスト」を引数で必ず受け取るようになっているため、それを login
関数の引数にそのまま指定してやれば良いです。「ログインしようとしているユーザー」に関しては、フォームからデータを送信して来たユーザーを AuthenticationForm
クラスが提供する get_user
メソッドにより取得することができますので、get_user
メソッドの返却値を login
関数の引数に指定してやれば良いです。そして、これらの引数を指定して login
関数を実行しているのが login(request, user)
の行となります。
login_view
はログインを実現することが目的のビューとなりますので、login
関数の実行により、その目的は果たせたことになります。が、それでもビューの関数は何らかのレスポンスを返却する必要があります。どんなレスポンスを返却するべきかはウェブアプリに応じて異なりますが、上記の例ではログイン後に自動的にユーザーの情報表示ページに遷移するよう、redirect
関数の返却値をレスポンスとするようにしています。
また、login_view
ではログインフォームを表示する際に forum/login.html
のテンプレートファイルを利用するようになっています。このテンプレートファイルは今まで存在しなかったファイルですので、後述の テンプレートの追加 / 変更 で追加の仕方について説明します。
ログアウト用のビュー logout_view
の追加
続いてログアウト用のビューの関数 logout_view
を追加していきます。
追加する logout_view
は下記のような関数になります。
from django.contrib.auth import logout
def logout_view(request):
logout(request)
return redirect('login')
logout_view
に関しては単純で、基本的には logout
関数を実行してユーザーのログアウトを行なっているだけです。ただし、ビューの関数である以上レスポンスを返却する必要があるため、redirect
関数の返却値を return
するようにしています。これにより、ログアウト後はログインページへのリダイレクトが行われることになります。
ユーザー登録用のビュー register_view
の変更
次は、register_view
の変更を行なっていきます。元々 register_view
はユーザー登録用のビューとして存在しており、ユーザー認証を行うようになったからといって別に役割が変わるわけではありません。
ただ、ユーザー登録を行なった際に、自動的にログインまで行われるとユーザーにとって便利ではあるので、ユーザー登録後にログイン処理を実施するようにしたいと思います。
具体的には、下記の変更前の register_view
を、
def register_view(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
form.save()
return redirect('users')
else:
form = RegisterForm()
context = {
'form': form
}
return render(request, 'forum/register.html', context)
下記のように変更します。変更点は、is_valid
が True
を返却した際に login
関数を実行するようになった点と、ログイン後にログインしたユーザーのページにリダイレクトされるよう redirect
関数の引数を変更した点となります(login
関数を実行するために、form.save
の返却値を user
で受け取るようにもしています)。
def register_view(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect('user', user.id)
else:
form = RegisterForm()
context = {
'form': form
}
return render(request, 'forum/register.html', context)
このように変更を行うことで、ユーザー登録に成功した際に自動的にログインが行われるようになります。
また、実装には影響はありませんが、register_view
で扱っていた RegisterForm
に指定する model
が変わったため、それに伴って register_view
で保存するユーザーの情報も変化することになります。具体的には、models.py
で定義した CustomUser
のインスタンス(レコード)が保存されるようになります。
ただ、ここに関してはモデルやモデルフォームが model
の指定に応じて自動的に対応してくれるため、前述の通り実装には影響はありません。
urls.py
の変更
ログインを実現するために必要なビューの変更は以上となります。
ただ、非ログインユーザーのアクセス制限を行なったり、ログイン中のユーザーを特定して投稿コメントと関連づける処理を追加したりするため views.py
の変更は以降も必要となります。
が、追加するビューとしては既に揃っているので、ここで追加した「ビューの関数」と「URL」とのマッピングを行なっておきたいと思います。このマッピングは forum/url.py
で設定することができ、この forum/url.py
を下記のように変更することで、追加した login_view
& logout_view
と URL とのマッピングを行ないます。
from django.urls import path
from . import views
urlpatterns = [
path('', views.index_view, name='index'),
path('comments/', views.comments_view, name='comments'),
path('comment/<int:comment_id>/', views.comment_view, name='comment'),
path('users/', views.users_view, name='users'),
path('user/<int:user_id>/', views.user_view, name='user'),
path('register/', views.register_view, name='register'),
path('post/', views.post_view, name='post'),
path('login/', views.login_view, name='login'),
path('logout/', views.logout_view, name='logout'),
]
変更前の forum/url.py
に比べ、 urlpatterns
に末尾から2つの要素の追加を行なっています。
これにより、下記 URL を指定した際にログインフォームが表示され、
http://localhost:8000/forum/login/
下記 URL を指定した際にはログアウトが実施されるようになります。
http://localhost:8000/forum/logout/
この辺りの URL マッピングに関しては下記ページで解説していますので、詳細は下記ページを参照していただければと思います。

テンプレートの追加
先ほど追加した login_view
関数では、ログインフォームを表示する際に forum/login.html
のテンプレートファイルを利用するようになっています。ただ、このテンプレートファイルは現状存在していないので、ここでこのテンプレートファイルを追加したいと思います。
また、forum/url.py
の変更によって /forum/login/
と /forum/logout_view/
のページが追加されたことになるため、これらのページへのリンクをナビゲーションバーに追加したいと思います。
login.html
の追加
まずは login.html
を追加したいと思います。手順は単純で、forum/templates/forum/
のフォルダの下に login.html
という名前のファイルを新規作成し、ファイルの中に下記を記述すれば良いだけになります。これで login.html
は完成です。
{% extends "forum/base.html" %}
{% block title %}
ログイン
{% endblock %}
{% block main %}
<h1>ログイン</h1>
<form action="{% url 'login' %}" method="post">
{% csrf_token %}
<table class="table table-hover">{{ form.as_table }}</table>
<p><input type="submit" class="btn btn-primary" value="送信"></p>
</form>
{% endblock %}
このテンプレートファイルの作りは、他のフォームを扱うテンプレートファイル(register.html
や post.html
)とほとんど同じです。url
タグに指定する URL の名前が 'login'
となっているだけです。ということで、login.html
に対する詳細な解説はここでは省略させていただきます。
テンプレートに関しては下記ページで詳細を解説していますので、テンプレートについて詳しく知りたい方は下記ページを参照していただければと思います。

base.html
の追加
次はナビゲーションバーへのリンクの追加を行っていきたいと思います。
この掲示板アプリではナビゲーションバーを全てのページで表示するようになっています。これは、base.html
がナビゲーションバーを表示するように作られており、各ビューの関数から利用される全てのテンプレートファイルが base.html
を継承するようになっているからになります。この base.html
に関しても forum/templates/forum/
に存在しています。
ということで、base.html
のナビゲーションバーにリンクを追加すれば、全ページで表示されるナビゲーションバーにリンクが追加されることになります。そして、このリンクの追加を行った base.html
が下記となります。base.html
に関してもこれで完成となります。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<header>
<nav class="navbar navbar-expand navbar-dark bg-primary">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link navbar-brand" href="{% url 'index'%}">掲示板</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'users'%}">ユーザー一覧</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'comments'%}">コメント一覧</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'post'%}">コメント投稿</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'register'%}">ユーザー登録</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'login'%}">ログイン</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'logout'%}">ログアウト</a>
</li>
</ul>
</nav>
</header>
<main class="container my-5 bg-light">
{% block main %}{% endblock %}
</main>
</body>
</html>
変更前の base.htm
に対して後ろ側2つの li
要素の追加を行っています。
href="{% url 'login'%}"
と href="{% url 'logout'%}"
部分がリンクの設定部分で、render
関数での HTML 生成時に、それぞれ 'login'
と 'logout'
という名前から URL が逆引きされ、その逆引き結果の URL のリンクが ログイン
というテキストと ログアウト
というテキストにそれぞれ設定されることになります。
そのため、ナビゲーションバーの ログイン
をクリックすれば結果的に login_view
が実行されてログインフォームが表示され、ログアウト
をクリックすれば logout_view
が実行されてログアウトが行われることになります。
非ログインユーザーのアクセス制限
以上の変更により、ログインを実現するためのモデル・フォーム・ビュー・テンプレートが揃ったことになり、ひとまず掲示板アプリでログインを実現することができるようになったことになります。
ただ、現状ではログインしていないユーザーもコメントの投稿やページの閲覧ができるようになっているため、次は非ログインユーザーのアクセス制限を行なっていきたいと思います。
この非ログインユーザーのアクセス制限は、login_required
というデコレータを利用することで実現することができます。要は、ビューの関数の定義の先頭に @login_required
を記述しておけば、そのビューの関数が表示するページに非ログインユーザーがアクセスできなくなります。これは、@login_required
の設定により、非ログインユーザーがそのページにアクセスしようとした際に他のページへ強制的にリダイレクトされるようになるからになります。ただ、どのページ(どの URL)へリダイレクトさせるかは別途 settings.py
での設定が必要になります。
ということで、非ログインユーザーのアクセス制限は、views.py
でアクセス制限したいページを表示するビューの関数に @login_required
を設定し、あとは settings.py
でリダイレクト先の設定を行うことで実現できることになります。
スポンサーリンク
@login_required
でのアクセス制限
まずは views.py
を変更してアクセス制限を実現していきます。
今回は、下記のように login_view
と register_view
の2つを除くすべてのビューの関数に @login_required
を設定するようにしたいと思います(関数の中身は省略しています)。
from django.contrib.auth.decorators import login_required
def login_view(request):
@login_required
def logout_view(request):
@login_required
def index_view(request):
@login_required
def users_view(request):
@login_required
def user_view(request, user_id):
@login_required
def comments_view(request):
@login_required
def comment_view(request, comment_id):
def register_view(request):
@login_required
def post_view(request):
login_view
に関してはログインを行うページを表示するためのビューとなるため、この関数にまで @login_required
を設定してしまうと誰もログインを行うことができなくなってしまいます。また、ユーザー登録は新規ユーザーが行うことを想定しているため、ユーザー登録もログインなしで行えるよう register_view
に関しても @login_required
を設定しないようにしています。
逆に、他のページに関してはログインを行わないとアクセスできないようになっています。もちろんコメントの投稿も行えませんし、コメント一覧やユーザー一覧もログインしてからでないと表示できないようになっています。
強制リダイレクト先の設定
続いて、非ログインユーザーが @login_required
が設定されたビューに対応するページを表示しようとしたときに遷移させるリダイレクト先の設定を行います。
この設定は、settings.py
への LOGIN_URL
の指定により実現することができます。LOGIN_URL
の指定は、認証モデルの設定 での変更で settings.py
への AUTH_USER_MODEL
の指定の追加を行ったはずですので、その下の行に追加するので良いです。これで settings.py
は完成となります。
LOGIN_URL = '/forum/login/'
上記では、LOGIN_URL
にルートパス形式で /forum/login/
を指定していますので、非ログインユーザーが @login_required
が設定されたビューに対応するページを表示しようとした際には下記 URL に強制的にリダイレクトされることになります。
http://localhost:8000/forum/login/
そして、この URL は urls.py
により login_view
にマッピングされているため、リダイレクト後にはログインフォームが表示され、ユーザーにログインを促すことができます。
もう少し詳細に言えば、リダイレクトが行われる際に上記の URL に対して ?next
クエリパラメータが付加されることになります。そして、これを利用してログイン後のリダイレクト先の URL をログイン前に表示しようとしたページに設定するようなことも可能となります。この辺りは下記ページで詳細を解説していますので、詳しく知りたい方は下記ページをご参照ください。

ログインユーザーとコメントの関連付け
最後に、コメント投稿フォームの変更を行なっていきたいと思います。
これまでのコメント投稿フォームでは、投稿者となるユーザーを選択するフィールドが表示されるようになっていました。そして、このフィールドを利用してユーザーはコメントを投稿する際に毎回投稿者を選択する必要がありました。
投稿者を手動で選択して設定する必要があったのは、ウェブアプリがコメントを投稿しようとしているユーザーを特定することができなかったからです。ログイン機能がなければ、ウェブアプリは基本的に誰から利用されているかを知る術がありません。
ですが、ログイン機能が搭載されれば、ログイン中のユーザーを特定することができることになります。したがって、投稿者をコメント投稿時にユーザーが手動選択して設定するのではなく、投稿者をコメント投稿を行なったログイン中ユーザーに自動的に設定するようなことが可能となります。
ということで、コメント投稿フォームから投稿者選択用のフィールドを削除し、さらに投稿者をログイン中のユーザーに自動的に設定できるようウェブアプリを変更していきたいと思います。
スポンサーリンク
コメント投稿フォームの変更
まずは、コメント投稿フォームの変更を行なっていきます。
これまでのコメント投稿フォームは、下記の forms.py
における PostForm
で定義されていました。ポイントになるのが exclude = ['date']
の部分で、Comment
モデルクラスには user
(投稿者)・text
(コメント本文)・date
(投稿日) の3つのフィールドが存在しているため、exclude = ['date']
の指定によってフォームには user
(投稿者)・text
(コメント本文) の2つが表示されるようになっています。
class PostForm(forms.ModelForm):
class Meta:
model = Comment
exclude = ['date']
widgets = {
'text': forms.Textarea
}
ログインを実現したことにより user
(投稿者) が不要となるため、これを表示しないようにするためには exclude = ['date']
部分を exclude = ['user', 'date']
or fields = ['text']
に変更してやれば良いです。
ということで、user
(投稿者) フィールドがフォームに表示されないように設定を行なった PostForm
は下記のようになります。このページでの forms.py
に対する最後の変更になるため、forms.py
全体を載せておきます。
from django import forms
from .models import Comment, max_age, min_age
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import get_user_model
User = get_user_model()
class RegisterForm(UserCreationForm):
class Meta:
model = User
fields = '__all__'#['username', 'email', 'age']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['age'].widget.attrs.update({
'max': max_age,
'min': min_age,
})
class PostForm(forms.ModelForm):
class Meta:
model = Comment
exclude = ['user', 'date']
widgets = {
'text': forms.Textarea
}
class LoginForm(AuthenticationForm):
pass
コメントとユーザーの関連付け
最後に、コメントとユーザーの関連付けを行なっていきます。
ビューの関数において、ログイン中ユーザーの情報は request.user
に設定されています。request
は Django フレームワークからリクエストのデータを受け取るための引数になります。さらに、request.user
は settings.py
で AUTH_USER_MODEL
に指定したモデルクラスのインスタンスとなります。つまり、今回の場合は models.py
で定義した CustomUser
のインスタンスということになります。
さらに、コメントを管理するモデルクラスの Comment
には user
フィールドが存在し、これは CustomUser
と Comment
の間に1対多のリレーションを設定するためのフィールドとなります。
Comment
から見ると CustomUser
は1対多における “1” の方の関係にあるため、Comment
のインスタンスの user
データ属性から CustomUser
のインスタンスを参照させるだけで、Comment
のインスタンスと CustomUser
のインスタンスの間にリレーションを構築することができます。今回の場合は、このリレーションはコメントとその投稿者の関係として表すことになります。
したがって、コメントの投稿時に Comment
モデルクラスのインスタンスの user
データ属性に request.user
を参照させれば、その Comment
のインスタンスとログイン中ユーザーの間にリレーションが構築され、ログイン中ユーザーを Comment
の投稿者として扱うことができるようになります。
この辺りのリレーションに関しては下記ページで詳しく説明していますので、リレーションについて詳しく知りたい方は下記ページを読んでみてください。

また、コメントの投稿の処理を行うのは views.py
における post_view
の役割となっていますので、上記のような処理を行うためには post_view
を次のように変更してやれば良いです。このページでの views.py
に対する最後の変更になるため、views.py
全体を載せておきます。
from django.shortcuts import redirect, render, get_object_or_404
from .forms import RegisterForm, PostForm, LoginForm
from .models import Comment
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, logout
from django.contrib.auth import get_user_model
User = get_user_model()
def login_view(request):
if request.method == 'POST':
form = LoginForm(data=request.POST)
if form.is_valid():
user = form.get_user()
if user:
login(request, user)
return redirect('user', user.id)
else:
form = LoginForm()
context = {
'form': form,
}
return render(request, 'forum/login.html', context)
@login_required
def logout_view(request):
logout(request)
return redirect('login')
def index_view(request):
return redirect(to='comments')
@login_required
def users_view(request):
users = User.objects.all()
context = {
'users' : users
}
return render(request, 'forum/users.html', context)
@login_required
def user_view(request, user_id):
user = User.objects.get(id=user_id)
context = {
'user' : user
}
return render(request, 'forum/user.html', context)
@login_required
def comments_view(request):
comments = Comment.objects.all()
context = {
'comments' : comments
}
return render(request, 'forum/comments.html', context)
@login_required
def comment_view(request, comment_id):
comment = get_object_or_404(Comment, id=comment_id)
context = {
'comment' : comment
}
return render(request, 'forum/comment.html', context)
def register_view(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect('user', user.id)
else:
form = RegisterForm()
context = {
'form': form
}
return render(request, 'forum/register.html', context)
@login_required
def post_view(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
comment = form.instance
comment.user = request.user
comment.save()
return redirect('comments')
else:
form = PostForm()
context = {
'form': form,
}
return render(request, 'forum/post.html', context)
ポイントは post_view
における form.is_valid()
が True
の場合の処理になります。
form
は PostForm
のインスタンスであり、PostForm
は forms.ModelForm
のサブクラスであるモデルフォームクラスとなります。モデルフォームクラスのインスタンスに is_valid
を実行させれば、そのインスタンスのデータ属性 instance
にはモデルクラスのインスタンスがセットされることになります(is_valid
が True
を返却する場合のみ)。より具体的には、モデルフォームクラスの定義で model
に指定したモデルクラス、今回の場合は Comment
のインスタンスが instance
にセットされることになります。
この辺りの解説は下記ページで行っていますので、詳しく知りたい方は下記ページをご参照ください。

つまり、上記の post_view
では、その Comment
のインスタンスを instance
から取得し、そのインスタンスのデータ属性 user
に request.user
を参照させる処理を行っていることになります。前述の通り、これにより、その Comment
のインスタンスの投稿者が request.user
、すなわちログイン中のユーザー(コメントを投稿したユーザー)に設定されることになります。
今回はログイン中ユーザーとコメントを関連付ける処理を例にビューの関数で request.user
を利用する例を示しましたが、この request.user
の使い所は非常に多いです。
例えば下記ページでは、ログイン中ユーザーのマイページを表示したり、ログイン中ユーザー以外のユーザー一覧を表示したりする例を示しています。その他にも様々な目的で利用できるため、ログイン中ユーザーを request.user
から取得できる点は是非覚えておいてください。

動作確認
最後に、変更後の掲示板アプリの動作確認を行なっておきましょう!
スポンサーリンク
データベースの初期化
今回は models.py
の変更を行なっていますので、最初にマイグレーションを実行する必要があります。ただし、今回は models.py
を大きく変更したため、マイグレーションに失敗する可能性が高いです。というか、失敗すると思います。
なので、今回はデータベースとマイグレーションの設定ファイルを全て削除して初期化し、それからマイグレーションを行いたいと思います。今まで登録したユーザー等が消えてしまうことになりますが、その点はご容赦ください。
また、今回紹介する手順でデータベースの初期化を行うとデータベースの中が完全に空になることになりますので注意してください。ただ、一番確実&楽にマイグレーションのエラーを解決する手順ではあるので、練習でウェブアプリを開発していてどうしてもマイグレーションのエラーが解決できない場合には有効な手順になると思います。
データベースの初期化手順
ということで、データベースを初期化する手順について説明していきます。ここではコマンドを利用した手順を説明していきますが、別にマウス操作等で同様の操作を行うのでも問題ありません。
ここで行うことは、下記の2つとなります。
- データベースファイルの削除
- マイグレーション設定ファイルの削除
まず、ターミナルアプリ等のコマンド実行が可能なアプリを開きます。そして、現在掲示板ウェブアプリの開発を行なっているプロジェクト testproject
のフォルダに移動してください。このフォルダには manage.py
が存在するはずで、いつもマイグレーション等のコマンドを実行しているフォルダになります。
さらに、移動後に下記コマンドを実行してデータベースファイルを削除します。
% rm db.sqlite3
SQLite3 を利用している場合、db.sqlite3
ファイルがデータベースの本体となり、このファイルでプロジェクト内で利用する各種テーブルや登録済みのレコードが管理されていることになります。
続いて、下記コマンドを実行してマイグレーション設定ファイルを削除します。
% rm forum/migrations/00*
Django では、models.py
変更後に makemigrations
コマンドを実行すると forum/migrations
フォルダの下に xxxx_yyyy.py
という形式の名前のファイルが作成されることになり、これがマイグレーション設定ファイルとなります。xxxx
は ID みたいなもので、ファイルが作成されるたびに 0001
・0002
・0003
・・・・といった感じで番号が1ずつ増えていくことになります。さらに、yyyy
の部分には models.py
の変更内容に基づいた文字列が設定されてファイルの命名が行われることになります。
また、上記のコマンドは「forum/migrations
フォルダの下のファイル名が 00
から始まるファイルを全て削除する」というコマンドになるため、マイグレーション設定ファイルの数が 100
個未満であれば全てのマイグレーション設定ファイルが削除されることになります。ここで重要なことは、forum/migrations
の下の __init__.py
を削除しないことになるため、それさえ守ればマウス操作等でマイグレーション設定ファイルを削除してしまっても問題ありません。
以上で、マイグレーション設定ファイルとデータベースファイルが削除され、次回のマイグレーションはデータベースがまっさらな状態で実施できることになります。本来 Django では models.py
の変更前後の差分に基づいてマイグレーション設定ファイルが作成され、さらにその設定ファイルに基づいてデータベースの操作が行われることになります。ただし、models.py
の変更内容によっては、その差分が上手く反映することができずにマイグレーションに失敗することがあります。
ですが、この失敗の原因は「差分がうまく反映できないこと」であって、データベースを初期化すれば models.py
の変更前後の差分に基づいてではなく models.py
全体からマイグレーション設定ファイルの作成およびデータベースの操作が行われることになるため差分の内容が関係なくなります。そのため、マイグレーションを初期化してからマイグレーションを実施してやれば、プロジェクト内の Python スクリプトや models.py
でのモデルの定義等に問題がなければ確実にマイグレーションを実施することができるようになります。
マイグレーションの実行
データベースの初期化が完了すればマイグレーションがすんなり実行できるはずです。
ということで、次はいつも通りの手順でマイグレーションの実行を行いましょう。マイグレーションは、プロジェクトフォルダの直下、データベースの初期化のために移動したフォルダで下記コマンドを実行することで行うことができます。
% python manage.py makemigrations
% python manage.py migrate
開発用ウェブサーバーの起動
続いても、いつも通りの手順で Django 開発用ウェブサーバーの起動を行いましょう。今いるフォルダで下記コマンドを実行すれば、Django 開発用ウェブサーバーが起動するはずです。
% python manage.py runserver
スポンサーリンク
アクセス禁止の確認
以上で、変更後の掲示板アプリが動作する環境が整ったことになります。
ということで、本題の掲示板アプリの動作確認を行なっていきましょう!
現状、まだログインを行なっていない状態になりますので、まずは非ログインユーザーのアクセスが禁止されていることを確認していきたいと思います!
まずウェブブラウザを開き、下記 URL を指定してログインページを開いてみてください。このページに対応する login_view
には @login_required
が設定されていないため普通に開くことができるはずです。
http://localhost:8000/forum/login/
続いて URL を指定して表示されたログインページのナビゲーションバーから ユーザー投稿
と ログイン
を除くリンクをクリックしてみてください。前回までのウェブアプリの動作確認時とは異なり、これらのリンクをクリックしてもそのページが表示されず毎回ログインページが表示されることになるはずです。
これがビューの関数に @login_required
を設定した効果であり、ログインしていないと @login_required
が設定されたビューに対応する URL のページが表示できなくなっていることを確認することができます。そして、このページが表示されない理由はリダイレクトが行われるからであり、そのリダイレクト先は settings.py
で LOGIN_URL
指定した URL となります。今回の場合は /forum/login/
を指定しているため、ログインページにリダイレクトされることになります。
ユーザー登録とログイン
次は、ログインを行うことで、@login_required
が設定されたビューに対応する URL のページが表示できるようになることを確認していきたいと思います。
ユーザー登録
ログインを行うためにはユーザー登録が必要となります。ということで、まずは ユーザー登録
リンクをクリックしてください。
これにより、forms.py
で定義した RegisterForm
に基づいたユーザー登録フォームが表示されることになります。RegisterForm
の fields
にはパスワード関連のフィールドは指定していませんが、パスワード入力用のフィールドが2つ(1つは確認用)が表示されていることが確認できるはずです。これは RegisterForm
が UserCreationForm
を継承しているからです。
このように、UserCreationForm
を継承したフォームを定義すれば、そのフォーム表示時にはユーザー登録時に必要となるパスワード関連のフィールドが自動的に表示されるようになります。
この表示されたユーザー登録フォームには適当なユーザー名・メールアドレス・年齢・パスワード(2回)を入力して 送信
ボタンをクリックしてください。パスワードは *
記号で隠されて表示されることになります。これも UserCreationForm
から継承された機能となります。
ログイン後の動作
各フィールドに適切な値が入力された状態で 送信
ボタンがクリックされれば、ユーザーの登録、すなわちユーザーのレコードのデータベースへの保存が実施されたのち、ログイン処理が実行されることになります。そして、その後に、ログイン中ユーザーの情報ページが表示されることになります。
このページ自体、ログインを行う前には表示することができなかったページになりますが、ログインが行われたことにより表示できるようになったことが確認できます。また、ナビゲーションバーのリンクをクリックすれば、ログイン前に表示できなかったページも表示可能になっていることが確認できると思います(ログアウト
をクリックするとログアウトされてしまうので注意してください)。
このように、ログイン機能を搭載することで、ログインの有無によって表示できるページのアクセス制限を行うことができます。このアクセス制限がログイン機能搭載で得られるメリットの1つになります。
登録されたレコードの確認
さて、先ほどユーザー登録フォームでユーザーの登録を行いましたが、次はこの登録によってデータベースに保存されたレコードを確認しておきたいと思います。
まずユーザー登録フォームは forms.py
で定義した RegisterForm
に基づいて表示されることになります。そして、この RegisterForm
の model
には CustomUser
が指定されています。また、RegisterForm
を扱うビューの関数 register_view
では、フォームからデータが送信されてきた際に、そのデータから RegisterForm
のインスタンスを生成するようになっています。そして、このインスタンスに save
メソッドを実行させるようになっています。従って、ユーザー登録フォームから 送信
ボタンのクリックによってデータが送信されてきた際には、forum_customuser
にテーブルに、ユーザーがフォームに入力したデータに基づいて生成された CustomUser
のインスタンスがレコードとして保存されることになります。
この辺りのモデルとデータベースの関連性に関しては下記ページで解説しているので、詳しく知りたい方は下記ページを参照していただければと思います。

実際の、ユーザー登録後の forum_customuser
テーブルは下図のようなものになっています。
テーブルが大きいので全体は示せていませんが、ここで私が伝えたいポイントは2つになります。
まず1つ目が、見慣れないフィールドが存在する点になります。上の図のテーブルで表示されているフィールド(password
・last_login
・is_superuser
・first_name
)は全て、CustomUser
で定義したフィールドには存在しません。これらは全て、CustomUser
のスーパークラスである AbstractUser
が持つフィールドであり、ユーザーを管理する上で必要となるフィールドとして Django フレームワーク側にあらかじめ用意されているものになります。上の図はテーブルの一部であり、他にも様々なフィールドが存在しているのですが、ここで伝えたいことは、AbstractUser
を継承すればユーザーを管理する上で必要になるフィールドが予め用意されていることになり、ユーザーを管理するモデルクラスの定義が楽になるという点になります。
実は、AbstractUser
は AbstractBaseUser
のサブクラスであり、この AbstractBaseUser
を継承してユーザーを管理するモデルクラスを定義するようなことも可能です。この辺りの説明に関してはは下記ページで詳しく解説していますので、詳しく知りたい方は読んでみてください。

ポイントの2つ目は password
フィールドの値になります。私がユーザー登録時にパスワードとして入力したのは yh123456
になります。それに対し、forum_customuser
の password
フィールドに保存されている値は下記のようなものになっています。
pbkdf2_sha256$320000$tasKoZe1sM6InxdXUgE7Zi$QhzNvJje7E+O64veEINP9kB7CSepZ0wmIDLGqJ0E7E8=
意味不明な文字列になっていますが、この「意味不明な文字列に変換されて元のパスワードが分からない」という点がポイントになります。要はパスワードが暗号化(ハッシュ化)されているということです。これにより、万が一データベースの中身の情報が流出したとしても利用者のパスワードの流出は防ぐことができます。
このパスワードの暗号化が行われているのは、RegisterForm
が UserCreationForm
のサブクラスであることが理由となります。UserCreationForm
の save
メソッドを実行された際にパスワードの暗号化が行われ、その暗号化の結果がデータベースに保存されることになります。つまり、この password
フィールドの暗号化は UserCreationForm
の持つ機能です。
したがって、単に forms.ModelForm
のサブクラスとして RegisterForm
を定義した場合、そのフォームでパスワード入力を受け付けるようにしたとしても、そのパスワードは暗号化されません。そうなると、データベースの中身の情報が流出した際に、アプリ利用者のパスワードまで流出してしまうことになります…。
より正確にいうと、この password
フィールドの暗号化は、UserCreationForm
の save
メソッドから AbstractBaseUser
のメソッドが実行されることで実現されることになります
forms.ModelForm
の save
メソッドからは、その AbstractBaseUser
のメソッドが実行されないため、結果的に暗号化が行われないことになります
ここで伝えたいことは、特にウェブアプリを公開する際にはセキュリティ対策をしっかり講じておくことが重要であるという点と、そのセキュリティ対策は Django に予め用意されたクラスやメソッドを利用することで実現できることが多いという点になります。その例の1つが、上記の UserCreationForm
となります。
こういったセキュリティ対策を、自身で実装することなく、単に利用するだけで実現できるという点も、ウェブアプリフレームワークを利用する大きなメリットの1つになります。
コメントの投稿
続いて、コメントの投稿の動作確認を行います。
今回ログイン機能を搭載したことで、コメント投稿時には投稿者をユーザーが手動で指定するのではなく、ログイン中のユーザーに応じて自動的に投稿者が設定されるようになっています。この点の動作を確認していきましょう!
まずは、ナビゲーションバーの コメント投稿
リンクをクリックしてコメント投稿フォームを表示し、適当なコメントを入力して 送信
ボタンをクリックしてください。ここでは投稿者を指定していない点がポイントになります。
送信
ボタンをクリックすれば、コメントの投稿が完了してコメント一覧ページが表示されるはずです。で、ここで注目していただきたいのが先ほど投稿したコメントの 投稿者
列になります。ここに、ログイン中ユーザーのユーザー名が表示されているはずです。
現状、登録ユーザーが一人だけなので少しわかりにくいかもしれませんが、登録ユーザーが複数人であっても、この投稿者には投稿したユーザーのユーザー名が正しく表示されることになります。これは、コメント投稿時に実行される post_view
の中でリクエストのデータからログイン中ユーザーを取得し、そのユーザーを投稿されたコメントの投稿者に設定しているからになります。
このように、ログイン機能を搭載していれば、ログイン中のユーザーはビューから簡単に取得することができ、それによって様々な機能を実現することができます。上記のようなコメント投稿の投稿者の自動設定についてもそうですし、ログイン中ユーザーに応じて表示するページを切り替えるようなことも簡単に行えます。
このような、ログイン中ユーザーを取得できるようになることでウェブアプリで実現可能な機能の幅を広げられる点も、ログイン機能のメリットの1つになります。
スポンサーリンク
ログアウトと再ログイン
次はログアウトを実施していきましょう!
ログアウト
ログアウトはナビゲーションバーの ログアウト
リンクをクリックすることで実現することができます。
ログアウト
リンクをクリックすれば、ログアウトが実行されてログインフォームが表示されることになります。ここで、ナビゲーションバーから コメント一覧
リンクなどをクリックしたとしても、再びログインフォームが表示されることを確認することができると思います。この点から、ログアウト
リンクのクリックによってログアウトが実施されていることが確認できます。
ログイン
続いて、ログインフォームからのログインが実現可能であることを確認していきたいと思います。
おそらく、今ログインフォームが表示されているはずなので、ユーザー登録時に指定したユーザー名とパスワードを入力して 送信
ボタンをクリックしてください。ここでもパスワードが *
記号で表示されており、これは LoginForm
が AuthenticationForm
を継承していることが理由となります。
登録済みのユーザー名とパスワードが入力されていれば、送信
ボタンクリックによってログインしたユーザーの情報ページが表示されるはずです。
さらに、ログアウト実施後は表示できなかったコメント一覧ページなども表示できることが確認できると思います。こういった点より、ユーザー登録をしておけばログインフォームからのログインが行えることも確認できると思います。
補足しておくと、AuthenticationForm
を継承したフォームで入力されたパスワードはユーザー登録時と同様のアルゴリズムで暗号化され、その暗号化された結果とデータベースのレコードとの間で照合が行われることになります。そして、ユーザー名とパスワードが一致するレコードが存在すれば認証 OK と判断されます。このような処理が行われるため、データベースに保存されているパスワードが暗号化されていても正しく認証を行うことが可能となっています。
ただし、これはデータベースに保存されているパスワード or 照合時に利用されるパスワードの一方でも暗号化されていなければ正しく認証が行われないということを意味しています。つまり、ログイン機能を実現するためには、AuthenticationForm
と UserCreationForm
とをセットで利用する、すなわち、ログインフォームは AuthenticationForm
のサブクラスとして、ユーザー登録フォームは UserCreationForm
のサブクラスとして定義する必要があります。
認証の失敗
最後に、認証に失敗した場合の動作も確認しておきたいと思います。
まず ログアウト
リンクをクリックしてログアウトを実施し、続いて ログイン
リンクをクリックしてログインフォームを表示してください。そして、先ほどはユーザー登録時に “指定した” ユーザー名とパスワードをフォームに入力しましたが、ここではユーザー登録時に “指定していない” ユーザー名とパスワードを入力して 送信
ボタンをクリックしてください。
この場合は認証に失敗し、下の図のように再度ログインフォームが表示されることになります。
そして、その後にナビゲーションバーで、例えば コメント一覧
などの他のページのリンクをクリックしてみてください。この場合、リダイレクトが行われて再びログインフォームが表示されることになるはずです。この動作より、認証に失敗した際にはログイン処理が実行されていないことが確認できます。認証に失敗した場合にも誤ってログイン処理が実行されているとアクセス制限をかける意味もありませんので、動作確認時にはこういった認証失敗時の動作も確認しておくことをオススメします。
まとめ
このページでは、掲示板アプリへのログイン機能の搭載手順について解説しました!
他のページでログインについては解説しているので、Django 入門の連載の中でログインについて解説するかどうかは正直迷いました…。が、やはりウェブアプリの開発においてログインは非常に重要であるため、解説ページを用意することにしました。
このページでは掲示板アプリへのログイン機能の搭載手順という観点に絞って解説を行なっていますので、ログイン機能の全体像を知りたい方は下記ページを参照していただければと思います。

また、現状ウェブアプリのビューは関数ベースで開発を進めていますが、クラスベースで開発することもでき、その場合のログイン機能の実現方法については下記ページで解説しています。こちらも興味があれば読んでみてください。

いつもあなたが利用しているウェブアプリもログイン機能を搭載しているものが多いのではないかと思います。そういったアプリも、ログイン機能の搭載方法を知っていれば自身の手で開発することもできるようになります。
また、今回は AbstractUser
・AuthenticationForm
・UserCreationForm
といった、Django フレームワークから提供されるクラスも多く利用しました。こういったクラスを利用するメリットについても理解していただけたのではないかと思います。これらの他にも、Django フレームワークからは便利なクラスがたくさん提供されているため、今後そういったクラスの紹介もしていきたいと思います。
次の Django 入門の連載においては「管理画面」について説明していきたいと思います。お試しでウェブアプリを開発するだけであれば管理画面を使わないことも多いかもしれないですが、ウェブアプリを運営していくためには管理画面も重要となります。次の連載のページは下記リンクから読むことができますので、次の連載もぜひ読んでみてください!
