Djangoでユーザー認証を実装する

背景

今実装しているアプリケーションでは、ユーザー認証機能が必須です。
実践Djangoのサンプルアプリケーションで、とても簡潔に実装されていたので、こちらにまとめておきます。

認証用アプリケーションの追加

まず認証用アプリケーションを追加します。

1
$ docker-compose run --rm web python manage.py startapp accounts

作成されたファイルを確認します。

1
2
3
4
5
6
7
8
$ find accounts -type f
accounts/migrations/__init__.py
accounts/models.py
accounts/__init__.py
accounts/apps.py
accounts/admin.py
accounts/tests.py
accounts/views.py

アプリケーションを追加するためにINSTALLED_APPSに追加します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ vi example/settings.py
...
...(省略)...

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django_extensions',
'accounts.apps.AccountsConfig',
]
...(省略)...
...

アプリケーションの追加が完了しました。

URLの設定

Djangoが提供しているビューを利用して、認証用アプリケーションのURLを設定します。

Djangoはログイン用のビューとしてLoginView、ログアウト用のビューとしてLogoutViewというクラスを提供してくれています。

accounts/urls.pyを以下のように書き換えます。

1
2
3
4
5
6
7
8
9
10
from django.contrib.auth.views import LoginView, LogoutView
from django.urls import path

urlpatterns = [
path('login/', LoginView.as_view(
redirect_authenticated_user=True,
template_name='accounts/login.html'
), name='login'),
path('logout/', LogoutView.as_view(), name='logout')
]

pathの第3引数のname=はURLを逆引きするときの名前を指定します。

では作成したaccounts/urls.pyをプロジェクト配下のurls.pyで読み込むようにします。

1
2
3
4
5
6
7
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('accounts.urls')),
]

テンプレートの作成

ログイン画面のテンプレートaccounts/login.htmlをまだ実装してないので、こちらを実装します。

bootstrapを利用して見た目を調整する

見た目を整えるためにbootstrap5を利用しようと思います。Djangoで利用するにはdjango-bootstrap5というパッケージをインストールします。

パッケージのインストール

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ docker-compose run --rm web poetry add django-bootstrap5
Creating example_web_run ... done
Skipping virtualenv creation, as specified in config file.
Using version ^21.3 for django-bootstrap5

Updating dependencies
Resolving dependencies... (22.2s)

Writing lock file

Package operations: 3 installs, 0 updates, 0 removals

• Installing soupsieve (2.3.1)
• Installing beautifulsoup4 (4.10.0)
• Installing django-bootstrap5 (21.3)

インストール完了しました。

INSTALLED_APPSに追加する

利用するためにはINSTALLED_APPSへの追加が必要です。

1
2
3
4
5
INSTALLED_APPS = [
...(省略)...
'accounts.apps.AccountsConfig',
'django_bootstrap5'
]

テンプレートの追加

accounts/templates/accounts/login.htmlを追加します。

レイアウトファイルの追加

ページの統一感を持たせるためレイアウトファイルを追加します。templates/base.htmlというファイルを追加します。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{% load static %}
{% load django_bootstrap5 %}
<html>
<head>
...(省略)...
</head>
<body>
...(省略)...
<main>
<div class="container">
{% block main %}{% endblock %}
</div>
</main>
</body>
</html>

accounts/login.htmlの追加

accounts/templates/accounts/login.htmlは先程作成したレイアウトファイルのmainブロックを上書きします。

1
2
3
4
5
6
7
8
9
10
11
12
{% extends "base.html" %}
{% load django_bootstrap5 %}

{% block main %}
<h2>ログイン</h2>
<form method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />
{% bootstrap_form form %}
{% bootstrap_button button_type="submit" content="ログイン" %}
</form>
{% endblock %}

作成したので、ログインページにアクセスしてみます。

テンプレート検索ディレクトリの追加

アクセスしてみるとTemplateDoesNotExist at /accounts/login/というエラーが発生してしまいます。理由はレイアウトファイルを置いたディレクトリはテンプレート検索パスに含まれていないため、テンプレートが見つからないからです。

templatesディレクトリをテンプレート検索パスに追加します。example/settings.pyTEMPLATESリストのDIRSを以下のように変更します。

1
'DIRS': [BASE_DIR / 'templates'],

再度アクセスすると、無事ログイン画面が表示されました!

動作確認

以前にpython manage.py createsuperuseradminユーザーを作成していたので、adminでログインできることを確認します。

ログインフォームにユーザー名とパスワードを入力してsubmitすると、認証は通ったようですが、/accounts/profile/というページにリダイレクトされ、Page not foundエラーが表示されてしまいました。

ログイン後のリダイレクトURL設定

ログイン後のリダイレクトURLはsettings.LOGIN_REDIRECT_URLで指定します。デフォルトの値を確認してみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ docker-compose run --rm web python manage.py shell_plus
Creating example_web_run ... done
# Shell Plus Model Imports
from django.contrib.admin.models import LogEntry
from django.contrib.auth.models import Group, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.contrib.sessions.models import Session
# Shell Plus Django Imports
from django.core.cache import cache
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import transaction
from django.db.models import Avg, Case, Count, F, Max, Min, Prefetch, Q, Sum, When
from django.utils import timezone
from django.urls import reverse
from django.db.models import Exists, OuterRef, Subquery
>>> settings.LOGIN_REDIRECT_URL
'/accounts/profile/'

デフォルトでは/accounts/profile/になっていることがわかりました。こちらの設定を上書きします。

1
2
3
LOGIN_URL = '/accounts/login/'
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'

LOGIN_URLはデフォルトと同じですが明示的に記載しておきます。LOGOUT_REDIRECT_URLはデフォルトでは指定されていませんでしたので/に指定しました。

再度/accounts/login/にアクセスします。先程すでに認証されているので、TOPページにリダイレクトされました。

まとめ

Djangoが提供しているビューを利用してユーザー認証を実装しました。テンプレートを作成したくらいで、ほとんど実装することなく認証ページを作成することができました。とても便利だと思います。

次回はメールアドレスで認証するように変更してみたいと思います。

参考図書