【Django】クラスベースビュー(class-based view):LoginViewを使用したログイン機能の実装

概要

クラスベースビュー(class-based view)でログイン機能を作成したのでまとめた。
ログイン機能は、Djangoがもともと用意しているLoginViewクラスを使用する。
LoginViewの場合、ListViewのように継承してビュークラスを作成する必要がなく簡単にログイン機能を実装できる。

LoginViewをどのように使用するのか、また処理イメージについて紹介する。

 

前提

以下の作成したプロジェクトをもとにLoginViewを追加している。

あわせて読みたい

概要 クラスベースビュー(class-based view)で一覧検索機能を作成したのでまとめた。 一覧検索機能は、Djangoがもともと用意しているListViewクラスを継承してを作成する。   前提 Djang[…]

 

 

ログイン機能処理イメージ

今回実装するログイン機能の画面イメージは以下となる。
入力チェックやエラーメッセージはLoginViewを呼び出せば自動で用意してくれる機能になる。

画面

ログイン画面

 

入力チェックエラー

 

ログイン失敗

 

ログイン成功

タスク一覧画面に遷移する。(前回の記事で実装した一覧画面)

 

 

アカウント機能のアプリ作成

機能概要

以下の機能をもつアカウントアプリをDjangoプロジェクトに追加する。

・ログイン
・ログアウト
・会員登録

今回はアカウントまわりの機能のうち、ログイン画面を対象とする。

 

アプリ作成

仮想環境を有効にした状態で以下のコマンドを実行し、【accounts】アプリを作成する。

ターミナル


python3 manage.py startapp accounts

 

アプリ追加時の設定

作成したaccountsアプリをDjangoに認識させるため、各種設定を行う。

アプリの登録

プロジェクトのsettings.pyに以下を追加する。

settings.py


INSTALLED_APPS = [
    (略)
    "accounts.apps.AccountsConfig"
]

 

accountsのルーティング追加

プロジェクトのurls.pyに以下を追加する。

urls.py


urlpatterns = [
    (略)
    path("accounts/", include("accounts.urls"))
]

上記設定により、「http://localhost:8000/accounts/」にリクエストすると、accounts/urls.pyにルーティングされる

 

その他

以下の階層にファイルとフォルダを作成する。

・accounts/templates ※フォルダ
・accounts/urls.py

 

 

ログイン機能の実装

LoginViewの処理イメージ

LoginViewを使用した際のざっくりとした処理のイメージが以下になる。
尚、Formに関する情報は後述する。

▲ログイン機能の全体像

 

①リクエスト
サーバーを起動した状態で「http://localhost:8000/accounts/login/」へリクエストする。

②アプリURLへルーティング
リクエストされたURLパターンを確認して、accountsアプリのurls.pyへルーティング。

③LoginViewの呼び出し
リクエストされたURLに紐づくLoginViewを呼び出す。
※LoginViewはDjangoがもともと用意している機能で、views.pyにて継承したクラスを作成する必要なく実装できる。

④ログイン画面表示
GETリクエストの場合、templates/registration配下のlogin.htmlを返却する。

⑤ログイン成功時
POSTリクエストでログイン認証成功の場合、settings.pyのLOGIN_REDIRECT_URL定数に定義したビューへリダイレクトされる。

 

LoginView機能まわりの実装

サーバー側

LoginViewを使用してログイン画面を表示するために、以下を実装する。

ルーティング

accounts/urls.py


from django.urls import path
from django.contrib.auth import views

urlpatterns = [
    path("login/", views.LoginView.as_view(), name="login")
]

「django.contrib.auth」というパッケージ内にアカウントまわりの機能が用意されている。
「django.contrib.auth」パッケージのLoginViewと「login/」URLを紐づけている。

上記により、サーバーを起動して「http://localhost:8000/accounts/login/」にアクセスすると、LoginViewクラス内の処理を呼ぶ。

もともと用意されているLoginViewクラスを使用するため、views.pyを実装する必要がない

 

リダイレクト先URLの設定

settings.py


LOGIN_REDIRECT_URL = "top"

ログイン成功後の挙動について定義しておかないと、エラーになってしまう。
settings.pyに「LOGIN_REDIRECT_URL」という定数を用意して、リダイレクトさせたいURL又はビュー名を設定する。

上記の”top”は以下のタスク一覧表示ビューを参照させている。

todoapps/urls.py


 from django.urls import path
from . import views


urlpatterns = [path("tasks/", views.TodoTopView.as_view(), name="top")]

 

画面側

accounts/templates/registrationフォルダ配下にlogin.htmlを作成する。
※LoginViewクラスが上記パスのlogin.htmlを参照するため、フォルダ階層とファイル名をそろえること

login.html


(略)
<div class="card-body">
  {% if form.non_field_errors %}
  <div class="text-danger mb-3">{{ form.non_field_errors }}</div>
  {% endif %}
  <form method="post">
    {% csrf_token %}
    <div class="form-group mb-3">
      <label for="username">名前</label>
      <input type="text" class="form-control" name="username" placeholder="名前" value="{% if form.username.value %}{{ form.username.value }}{% endif %}">
      {% for error in form.username.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <div class="form-group">
      <label for="password">パスワード</label>
      <input type="password" class="form-control" name="password" placeholder="パスワード" value="{% if form.password.value %}{{ form.password.value }}{% endif %}">
      {% for error in form.password.errors %}
      <div class="text-danger">{{ error }}</div>
      {% endfor %}
    </div>
    <button type="submit" class="btn btn-primary mt-3">ログイン</button>
  </form>
</div>
(略)

ログイン画面の実装まわりで使用されている部品の詳細は以下になる。

 

 {% if form.non_field_errors %}
<div class=”text-danger mb-3″>{{ form.non_field_errors }}</div>
{% endif %}

ユーザーテーブルに存在しない名前とパスワードでログインしようとした場合、non_field_errorにエラーメッセージが格納される。

 

 {% csrf_token %}

上記はクロスサイトリクエストフォージェリ(CSRF)攻撃からサイトを保護するために使用される。
定義しない場合、エラーになってしまう。

攻撃内容の詳細については以下を参照。

IPA 独立行政法人 情報処理推進機構

情報処理推進機構(IPA)の「安全なウェブサイトの作り方 - 1.6 CSRF(クロスサイト・リクエスト・フォージェリ)…

 

<input type=”text” class=”form-control” name=”username” placeholder=”名前”>
<input type=”password” class=”form-control” name=”password” placeholder=”パスワード”>

LoginViewにより使用できるフォーム部品は「username」と「password」変数になる。
name属性に他の変数名で定義しないよう注意すること。

上記の「username」と「password」をLoginViewに紐づくURLへリクエストすることで、ログイン認証処理を行ってくれる。

 

{% for error in form.username.errors %}
<div class=”text-danger”>{{ error }}</div>
{% endfor %}

{% for error in form.password.errors %}
<div class=”text-danger”>{{ error }}</div>
{% endfor %}

上記は未入力の状態でサーバー通信した場合などでエラーメッセージが個別に格納される。

 

以上でログイン機能は実装できる。

 

ログイン処理補足

LoginViewについて、見えない処理の流れを少しだけ具体的にすると以下のような処理の流れになる。

ログイン画面表示処理イメージ

ログイン画面表示リクエストのGET通信がきた場合、LoginViewクラスは空のAuthenticationFormクラスを作成してログイン画面に渡す。
AuthenticationFormのusernameとpassword属性のフォームという箱を画面に渡しているイメージ。

 

ログイン認証処理イメージ

ログイン認証リクエストのPOST通信がきた場合、LoginViewクラスは空のAuthenticationFormクラスに画面入力値(フォーム情報)を代入する。

AuthenticationForm内で入力チェック処理等が見えないところで実行される。
入力チェックエラーの場合、エラー情報を含んだAuthenticationFormクラスを画面にかえす。

ログイン画面を再表示する際、formの中にエラー情報を含んでいるためエラーメッセージ領域にエラーメッセージが表示される。

入力チェックエラーではない場合、AuthenticationFormクラスからパラメータを取得してDB通信を行う。

DB通信して認証NGの場合、再度AuthenticationFormクラスにエラーメッセージをつめてログイン画面に返却する。
DB通信して認証OKの場合、リダイレクト処理を呼び出す。

 

 

 

スポンサーリンク