【django】ログイン認証機能:パスワード変更機能を組み込む方法

2021.04.08 /

【django】ログイン認証機能:パスワード変更機能を組み込む方法

本記事では、PythonのWebフレームワークdjangoを使って、ログイン認証機能におけるアカウントのパスワード変更機能を組み込む方法を解説していきます。

djangoには最初から使える様々な機能が備わっています。
もちろんログイン認証で使用する基本的な機能も備わっています。

初めから備わっている機能のうち、ここではアカウントパスワード変更機能をWebアプリケーションに組み込む方法を見ていきましょう。

Djangoに組み込まれているパスワード変更機能

Djangoには初めから様々な便利な機能が用意されています。

その中の一つ、アカウント認証に使われるパスワードの変更機能について解説していきます。

django.contrib.auth

django.contrib.authにより、djangoのアカウント認証で必要になる機能が提供されます。

django.contrib.authはフォルダの階層構造を示しており、仮想環境venvを使用している場合は、次のパスを示しています。

venv\Lib\site-packages\django\contrib\auth\

実際にdjango.contrib.authのフォルダ内を確認してみてください。
urls.pyやviews.pyなどたくさんのpyファイルが保存されていることがわかります。

これらフォルダ内のファイル(スクリプト)をプログラムの先頭でimportすることで、パスワード変更機能などが使えるようになるということです。

django.contrib.auth.views

django.contrib.auth内のviews.pyにアカウント認証で使われる様々な機能が記載されています。

今回のパスワード変更には、その中の以下2つのビュークラスを使用します。

  • PasswordChangeView
  • PasswordChangeDoneView

PasswordChangeView

クラスPasswordChangeViewはアカウントのパスワードを変更する処理を提供します。

class PasswordChangeView(PasswordContextMixin, FormView):
    form_class = PasswordChangeForm
    success_url = reverse_lazy('password_change_done')
    template_name = 'registration/password_change_form.html'
    title = _('Password change')

パスワードの変更が成功したら、urls.pyでnameがpassword_change_doneにページ遷移することがわかります。

PasswordChangeDoneView

クラスPasswordChangeDoneViewはアカウントのパスワード変更が成功した場合の処理を提供します。

class PasswordChangeDoneView(PasswordContextMixin, TemplateView):
    template_name = 'registration/password_change_done.html'
    title = _('Password change successful')

django.contrib.auth.urls

django.contrib.auth内のurls.pyには、アカウント認証で使用できるURLパターンが用意されています。

accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']

このように便利なurls.pyが用意されているのですが、本記事のパスワード変更機能では使用しません。

パスワード変更機能を追加してみよう

では実際にWebアプリケーションにパスワード変更機能を追加してみましょう。
以下記事で作成したWebアプリケーションに対してパスワード機能を追加します。

次の流れでWebアプリケーションにパスワード変更機能を追加していきます。

  1. urls.pyにパスワード変更クラスへのルーティングを追加
  2. パスワード変更及び完了を表示するテンプレートの作成
  3. base.htmlにパスワード変更ページへのリンクを貼る

最終的に完成するWebアプリケーションの画面遷移を以下に記します。

  1. ログイン後のトップページ
  2. djangoによるwebアプリケーション:トップ画面
  3. アカウント名をクリックして、プルダウンメニューを表示
  4. djangoによるwebアプリケーション:ユーザー名をクリックしたプルダウンメニュー表示
  5. パスワード変更画面
  6. djangoによるwebアプリケーション:アカウントパスワード変更画面
  7. パスワード変更完了通知画面
  8. djangoによるwebアプリケーション:アカウントパスワード変更完了画面

このWebアプリケーションを作成していきましょう。

urls.pyにルーティングを追加

まずはプロジェクトのurls.pyにdjango.contrib.auth.viewsの2つのクラス(PasswordChangeView、PasswordChangeDoneView)へのルーティングを追加します。

from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views    # 追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('five.urls')),
    path('accounts/', include('django.contrib.auth.urls')),
    path('accounts/password_change_form/', auth_views.PasswordChangeView.as_view(template_name='registration/password_change.html'), name='password_change_form'),    # 追加
    path('accounts/password_change_done/', auth_views.PasswordChangeDoneView.as_view(template_name='registration/password_change_finish.html'), name='password_change_done'), # 追加
]

上記urls.pyでは、django.contrib.authからviewsをインポート(auth_viewsとする)して、2つのクラスへのルーティングを追加しています。

各ルーティングではオプションtemplate_nameを使って、クラスで使用するテンプレートを指定しています。

すでに備わっているクラスを使用しているので、views.pyへの記入は一切必要ありません。

テンプレートの追加

urls.pyの記入ができたら、次にパスワード変更と完了を表示するテンプレートを作成します。

アプリケーションフォルダ内のtempletes\registration内にpassword_change.htmlとpassword_change_finish.htmlを次のように作成しましょう。

password_change.html

{% extends "five/base.html" %}
{% block content %}
<h2>Password Change</h2>
<form method="post" style="padding-left:20px;">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Change Password</button>
</form>
{% endblock %}

password_change_finish.html

{% extends "five/base.html" %}
{% block content %}
<h2>Password Change Done</h2>
{% endblock %}

これでパスワード変更機能の実装はほとんど完了となります。

ソースコード

上記で示したページ以外のページは、次に示すように変更します。

base.html

{% load static %}
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE-edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0">
    <title>OFFICE54</title>
    <link rel="stylesheet" href="{% static 'five/common.css' %}">
    <script src="https://kit.fontawesome.com/d86c726e71.js" crossorigin="anonymous"></script>
</head>
<body>
    <header>
        <div>
            <p>OFFICE</p>
        </div>
        <div>
            {% if user.is_authenticated %}
            <ul class="header-munu-ul">
              <li class="nav_item"><a href="#" class="menu-name js-dropdown"><i class="far fa-user-circle" style="margin-left:-10px;margin-right:10px"></i><small>{{ user.username }}</small></a>
                <div class="panel js-dropdown-menu">
                  <ul class="panel-inner">
                    <li class="panel_item"><a href="{% url 'logout' %}">Logout</a></li>
                    <li class="panel_item"><a href="{% url 'password_change_form'%}">Change Password</a></li>
                  </ul>
                </div>
              </li>
            </ul>
            {% else %}
            <p><a href="{% url 'login' %}">ログイン</a></p>
            {% endif %}
        </div>
    </header>

    <div id="wrapper">
      <nav id="nav-left">
        <div id="nav">
          <ul id="nav-ul">
            <li class="nav-li"><a href="{% url 'index' %}" class="nav-li-a"><i class="fa fa-tachometer" style="margin-right:15px"></i>Dashboard</a></li>
            <li class="nav-li"><a href="#" class="nav-li-a"><i class="fas fa-list" style="margin-right:15px"></i>App1</a></li>
            {% if user.is_authenticated %}
            <li class="nav-li"><a href="{% url 'password_change_form'%}" class="nav-li-a"><i class="fas fa-server" style="margin-right:15px"></i>User Password Change</a></li>
            {% endif %}
          </ul>
        </div>
      </nav>
      <main id="main">
          {% block content %}
          {% endblock %}
      </main>
    </div>

    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    <script type="text/javascript">
      $(function(){
        var $dropdown = $('.js-dropdown');
        var DURATION = 200; //アニメーションの速さ

        function fadeOutMenu(){
          $dropdown.removeClass('is-active')
            .next('.js-dropdown-menu')
            .stop()
            .slideUp(DURATION);
        }

        $('.js-dropdown').on('click', function(){
          var $self = $(this);
          if(!$self.hasClass('is-active')){
            fadeOutMenu();
          }
          $self.toggleClass('is-active')
            .next('.js-dropdown-menu')
            .stop().slideToggle(DURATION);
        })
        $(document).on('click touchend', function(event) {
          if (!$(event.target).closest('.js-dropdown').length) {
            fadeOutMenu();
          }
        });
      });
    </script>

</body>
</html>

common.css

common.cssに追加したコードを以下に記します。

a{
    text-decoration: none;
}

header .header-munu-ul{
  display: flex;
  height: 50px;
  list-style: none;
}
header li{
  align-items: center;
  justify-content: center;
}
header .menu-name{
  color: #898a8e;
  /*color: #0099FF;*/
  display: flex;
  align-items: center;
  justify-content: center;
  height: 50px;
  font-size: 20px;
  font-weight: 600;
}
header .nav_item{
    position: relative;
}
header .nav_item:hover{
  background-color: rgb(230,230,230);
}

header .panel{
  position: absolute;
  top:52px;
  left: 0;
  right: 0;
  margin: auto;
  overflow: hidden;
  width: 100%;
  display: none;
  background-color: white;
  z-index: 100;
  border-radius: 3px;
  box-shadow: 0 0px 20px rgba(0,0,0,0.2);
}
header .panel_item{
  display: block;
  margin: 20px 10px;
  font-size: 14px;
}
header .panel_item a{
  color: #0099FF;
  font-weight: 100;
}
header .panel_item a:hover{
  text-decoration: underline;
}
header .nav_item{
  display: block;
  width: 230px;
  text-align: center;
}
.is-active{
  color: orange !important;
}

まとめ

みなさんパスワード変更機能はきちんと追加することはできましたか。

本記事では、Djangoで最初から備わっている機能を利用することで、非常に簡単にパスワード変更機能を組み込むことができました。

ぜひそういった機能を利用して、Webアプリケーションの開発時間を短縮していきましょう。