Python + Djangoでパスワード変更画面を実装する   

Djangoでパスワード変更画面を実装してみます。

[Python + Djangoで独自のユーザ認証を実装する](https://qiita.com/tiguchi919/items/54424f619829a6d135fc)の続きです。

top画面にパスワード変更機能を追加します。DJangoのパスワード変更画面を利用します。

###1.accountsアプリケーション

パスワード変更機能をaccountsに追加することにしました。

(1)accounts\urls.py

パスワード変更用にurlsを追加します

```accounts\urls.py
from django.urls import path
from . import views

app_name = 'accounts'
urlpatterns = [
    path('', views.index.as_view(), name='index'),
    path('password_change/', views.PasswordChange.as_view(), name='password_change'), #追加
    path('password_change/done/', views.PasswordChangeDone.as_view(), name='password_change_done'), #追加
]
```

indexはviewをクラスベースにしたので変更しています

(2)accounts\views.py

```accounts\views.py
from django.shortcuts import render
from django.contrib.auth.views import PasswordChangeView, PasswordChangeDoneView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views import generic
from django.urls import reverse_lazy

class index(LoginRequiredMixin, generic.TemplateView):
    """メニュービュー"""
    template_name = 'accounts/top.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs) # 継承元のメソッドCALL
        context["form_name"] = "top"
        return context

class PasswordChange(LoginRequiredMixin, PasswordChangeView):
    """パスワード変更ビュー"""
    success_url = reverse_lazy('accounts:password_change_done')
    template_name = 'accounts/password_change.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs) # 継承元のメソッドCALL
        context["form_name"] = "password_change"
        return context

class PasswordChangeDone(LoginRequiredMixin,PasswordChangeDoneView):
    """パスワード変更完了"""
    template_name = 'accounts/password_change_done.html'
```

Point
:point_up_2: LoginRequiredMixinで、認証を必須としています
:point_up_2: パスワード変更、パスワード変更完了は、Djnagoに用意されているビューを利用しています。
:point_up_2:form_nameという変数を追加しています

(3)html

>password_change.html

パスワード変更画面です。

```django:templates\accounts\password_change.html
{% extends "commons/base.html" %}

{% block title %}パスワード変更{% endblock %}

{% block styles %}
{% endblock %}

{% block headertitle %}パスワード変更{% endblock %}

{% block content %}
<div class="row justify-content-md-center">
<div  class="col-6">
<form action="" method="POST">
    {{ form.non_field_errors }}
    {% for field in form %}
    <div class="form-group">
        <label class="col-6" for="{{ field.id_for_label }}">{{ field.label_tag }}</label>
        {{ field }}
        {{ field.errors }}
    </div>
    {% endfor %}
    {% csrf_token %}
    <div class="form-group row">
      <div class="col-sm">
        <button type="submit" class="btn btn-primary btn-block">送信</button>
      </div>
      <div class="col-sm">
        <button type="submit" class="btn btn-primary btn-block" onClick="javascript:history.go(-1);">戻る</button>
      </div>
    </div>
</form>
</div>
</div>
{% endblock %}
```

>password_change_done.html

パスワード変更のお知らせ画面です。

```django:templates\accounts\password_change_done.html
{% extends "commons/base.html" %}

{% block title %}パスワード変更完了{% endblock %}

{% block styles %}
{% endblock %}

{% block headertitle %}パスワード変更完了{% endblock %}

{% block content %}
<div class="row justify-content-md-center">
  <div  class="col-4">
    <p>パスワードを変更しました。</p>
    <div class="form-group row">
      <div class="col-6">
        <a class="btn btn-primary btn-block" href="{% url 'accounts:index' %}">トップへ</a>
      </div>
    </div>
  </div>
</div>
{% endblock %}
```

>base.html

views.pyで追加したform_nameによって、「パスワード変更」ボタンを自身の画面に表示しないようにしています

```django:templates\commons\base.html
{% load staticfiles %}
{% load bootstrap4 %}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
    <title>
      {% block title %}{% endblock %}
    </title>
    <style>
        body {
          padding : 10px;
        }
        div.container {
          min-width:600px;
        }
        nav ul#menu {
            margin:0px;
            padding:0px;
        }
        nav ul#menu li {
            float:right;
            list-style-type:none;
            text-decoration: underline;
            padding-left: 10px;
        }
        div.header {
          padding: 10px;
        }
        div.header * {
          color: white !important;
        }
        div.header .title {
          font-size: 1.2em;
        }
        {% block styles %}{% endblock %}
    </style>
</head>
<body>
<div class="container">
  <div class="inner">
    <div class="text-right">{{ form_name }}</div>
    <div class="row header mysystem-header">
      <div class="col-sm title">
        {% block headertitle %}{% endblock %}
      </div>
      <div class="col-sm">
        <nav class="pull-right">
            <ul id="menu">
              {% if user.is_authenticated %}
                <li><a href="{% url 'logout' %}" class="logout">ログアウト</a></li>
                {% if form_name == "password_change" %}
                {% else %}
                    <li><a href="{% url 'accounts:password_change' %}" class="logout">パスワードの変更</a></li>
                {% endif %}
              {% else %}
              {% endif %}
            </ul>
        </nav>
      </div>
    </div>
    <br/>
    <div class="content">
        {% block content %}{% endblock %}
    </div>
  </div>
</div>

<script src="{% static "js/jquery-3.3.1.min.js" %}"></script>
<script src="{% static "js/bootstrap.bundle.min.js" %}"></script>
{% block scripts %}{% endblock %}

</body>
</html>
```

###2.動作確認

サーバを起動

```cmd.prompt
(venv) C:\data\python\work\myproject>manage.py runserver
```

サイトにアクセス

http://localhost:8000/
![image.png](https://qiita-image-store.s3.amazonaws.com/0/246741/6ffe580e-225c-2d95-bbfe-3f6e23f4b084.png)

パスワード変更をクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/246741/a4e831b6-0202-1902-a29c-ca1931546cee.png)

パスワードを変更して送信
![image.png](https://qiita-image-store.s3.amazonaws.com/0/246741/b264c73e-30d4-98ee-8a60-7f27629c6c2b.png)

Djangoを使うと、認証情報のmodel、更新処理などを全部やってくれるのでとても楽に開発できそうです