Python + Djangoでパスワードリセット画面を実装する


Djangoのパスワードリセット機能を試します。

以下のリンクの続きです。
Python + Djangoで独自のユーザ認証を実装する
Python + Djangoでパスワード変更画面を実装する

1.パスワードリセットのフロー

パスワードリセットは、システム上で、メールアドレスを入力して送信し、ユーザはメール受信後に、本文に記載されているURLをクリックします。パスワード変更画面(パスワードリセット用)が開くので、パスワードを変更すると、パスワードリセット完了の画面が表示され完了します。

  • (1)パスワードリセット(メールアドレス入力)画面
  • (2)パスワードリセット(メール送信)画面
  • (3)メールのURLをクリック
  • (4)パスワードリセット(パスワード変更)画面
  • (5)パスワードリセット(パスワードリセット完了)画面

Djangoでは、このパスワードリセットも最初から実装されているのでそれを利用します。

2.実装

(1)accounts\urls.py

パスワードリセット用に、URLを4つ追加します。

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'),
    path('password_reset/', views.PasswordReset.as_view(), name='password_reset'), #追加
    path('password_reset/done/', views.PasswordResetDone.as_view(), name='password_reset_done'), #追加
    path('reset/<uidb64>/<token>/', views.PasswordResetConfirm.as_view(), name='password_reset_confirm'), #追加
    path('reset/done/', views.PasswordResetComplete.as_view(), name='password_reset_complete'), #追加
]

(2)accounts\views.py

パスワードリセット用に、views.pyを編集します。formクラスはDjangoで用意されているのでそのまま利用します。

accounts\views.py
from django.shortcuts import render
# PasswordResetView, PasswordResetDoneView, PasswordResetConfirmView, PasswordResetCompleteViewを追加
from django.contrib.auth.views import PasswordChangeView, PasswordChangeDoneView, PasswordResetView, PasswordResetDoneView, PasswordResetConfirmView, PasswordResetCompleteView
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'

# --- ここから追加
class PasswordReset(PasswordResetView):
    """パスワード変更用URLの送付ページ"""
    subject_template_name = 'accounts/mail_template/reset/subject.txt'
    email_template_name = 'accounts/mail_template/reset/message.txt'
    template_name = 'accounts/password_reset_form.html'
    success_url = reverse_lazy('accounts:password_reset_done')


class PasswordResetDone(PasswordResetDoneView):
    """パスワード変更用URLを送りましたページ"""
    template_name = 'accounts/password_reset_done.html'


class PasswordResetConfirm(PasswordResetConfirmView):
    """新パスワード入力ページ"""
    success_url = reverse_lazy('accounts:password_reset_complete')
    template_name = 'accounts/password_reset_confirm.html'


class PasswordResetComplete(PasswordResetCompleteView):
    """新パスワード設定しましたページ"""
    template_name = 'accounts/password_reset_complete.html'

# --- ここまで

(3)templates\accounts\password_reset_form.html

templates\accounts\password_reset_form.html
{% extends "commons/base.html" %}
{% block headertitle %}
  パスワードリセット
{% endblock %}
{% block content %}
<form action="" method="POST">
    {{ form.non_field_errors }}
    <p>メールを受信してパスワードの変更手続きを行います。</p>
    <p>メールアドレスを入力して送信ボタンを押してください。</p>
    {% for field in form %}
    <div class="form-group">
        <label for="{{ field.id_for_label }}">{{ field.label_tag }}</label>
        {{ field }}
        {{ field.errors }}
    </div>
    {% endfor %}
    {% csrf_token %}

    <pre>メールアドレスはあらかじめシステムに登録が必要です。
メールが届かない場合はシステム管理者に連絡してください。</pre>
    <br/>
    <div class="form-group row">
      <div class="col-6">
        <button type="submit" class="btn btn-primary btn-block">送信</button>
      </div>
    </div>
</form>
{% endblock %}

(4)templates\accounts\password_reset_done.html

templates\accounts\password_reset_done.html
{% extends "commons/base.html" %}

{% block headertitle %}
  パスワードリセット(メール送信)
{% endblock %}

{% block content %}
<form action="" method="POST">
<p>パスワードリセットのメールを送信しました。
<br/>
メールに記載されているリンクからパスワードの再設定を行ってください。
<br/>
<br/>
<br/>
<a class="btn btn-primary col-4" href="{% url 'accounts:index' %}">ログイン画面へ</a>
</p>
</form>
{% endblock %}

(5)templates\accounts\password_reset_confirm.html

templates\accounts\password_reset_confirm.html
{% extends "commons/base.html" %}
{% block content %}
<form action="" method="POST">
    {{ form.non_field_errors }}
    {% for field in form %}
    <div class="form-group">
        <label for="{{ field.id_for_label }}">{{ field.label_tag }}</label>
        {{ field }}
        {{ field.errors }}
    </div>
    {% endfor %}
    {% csrf_token %}
    <button type="submit" class="btn btn-primary btn-block">送信</button>
</form>
{% endblock %}

(6)templates\accounts\password_reset_complete.html

templates\accounts\password_reset_complete.html
{% extends "commons/base.html" %}
{% block content %}
<form action="" method="POST">
<p>
    パスワード再設定を完了しました。<br>
    <a class="btn btn-primary btn-block" href="{% url 'accounts:index' %}">ログイン</a>
</p>
</form>
{% endblock %}

以上で実装終わりです。

※パスワードリセット(メールアドレス入力)画面で入力するメールアドレスは、プロジェクトの認証ユーザに設定されているメールアドレスである必要があります。管理サイトからメールアドレスを設定しましょう。

3.動作確認

では、確認してみます。

(1)パスワードリセット(メールアドレス入力)画面

(2)パスワードリセット(メール送信)画面

(3)メール

送信されたメール。ユーザ:Djangoに設定したメールアドレスに送信されています。

django 様


下記URLよりサイトにアクセスし、パスワードの再設定を行ってください。


再設定用URL
http://localhost:8000/reset/MQ/4yb-77d2a24a746ddeceb805/

(4)パスワードリセット(パスワード変更)画面

(5)パスワードリセット(パスワードリセット完了)画面

パスワードリセットもDjangoは簡単です。