naritoブログ

このブログはPython/DjangoとBootstrap4で作成されました
Githubのソースコード

Djangoで、パスワード変更ページと忘れた際の再設定ページ

約28日前 2018年4月29日20:19
プログラミング関連

概要


Djangoで、会員登録機能を自作するの1つです。
前回、ユーザー情報閲覧・更新ページを作りました。
今回はパスワードの変更ページと、パスワードを忘れた際の再設定ページを作ります。

パスワードの変更


まず、パスワードの変更から実装していきます。

見た目


パスワード変更ページへのリンクが増えました
パスワード変更リンクが増えました

今のパスワードと、新しいパスワードを入力します。
現在のパスワードと、新しいパスワードを入力します

無事に変更できたら、このように表示されます。
OK!


urls.py


以下の2つが必要です。
    path('password_change/', views.PasswordChange.as_view(), name='password_change'),
    path('password_change/done/', views.PasswordChangeDone.as_view(), name='password_change_done'),


base.html


パスワード変更ページへのリンクが増えました。
        <ul class="navbar-nav mr-auto">
          {% if user.is_authenticated %}
          <li>
            <a class="nav-item nav-link" href="{% url 'register:user_detail' user.pk %}">ユーザー情報閲覧</a>
          </li>
          <li>
            <a class="nav-item nav-link" href="{% url 'register:user_update' user.pk %}">ユーザー情報更新</a>
          </li>
          <li>
            <a class="nav-item nav-link" href="{% url 'register:logout' %}">ログアウト</a>
          </li>
          <li>
            <a class="nav-item nav-link" href="{% url 'register:password_change' %}">パスワードの変更</a>
          </li>
          {% else %}
          <li class="nav-item">
            <a class="nav-item nav-link" href="{% url 'register:login' %}">
            ようこそ、ゲスト!ログインはこちら
          </a>
          </li>
          {% endif %}
        </ul>



forms.py


PasswordChangeFormという用意されたクラスを使うと簡単です。それと、Bootstrap4対応もしておきます。
from django import forms
from django.contrib.auth.forms import (
    AuthenticationForm, UserCreationForm, PasswordChangeForm
)
from django.contrib.auth import get_user_model
...
...
class MyPasswordChangeForm(PasswordChangeForm):
    """パスワード変更フォーム"""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'




views.py


こちらも、用意されたクラスを使えばすぐです。
importは、増えたものだけ書いています。
..
...
from django.contrib.auth.views import (
    LoginView, LogoutView, PasswordChangeView, PasswordChangeDoneView
)
...
...
from django.urls import reverse_lazy
...
...
from .forms import (
    LoginForm, UserCreateForm, UserUpdateForm, MyPasswordChangeForm
)
...
...
class PasswordChange(PasswordChangeView):
    """パスワード変更ビュー"""
    form_class = MyPasswordChangeForm
    success_url = reverse_lazy('register:password_change_done')
    template_name = 'register/password_change.html'


class PasswordChangeDone(PasswordChangeDoneView):
    """パスワード変更しました"""
    template_name = 'register/password_change_done.html'



password_change.html


一応htmlを作りましたが、再利用しちゃって良いかもしれませんね。
{% extends "register/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-lg">送信</button>
</form>
{% endblock %}


password_change_done.html


変更した、と伝えるだけです。
{% extends "register/base.html" %}
{% block content %}
<p>
    パスワードを変更しました。<br>
    <a class="btn btn-primary btn-lg" href="{% url 'register:login' %}">ログイン</a>
</p>
{% endblock %}



パスワード忘れ


パスワードを忘れた際の再発行機能も、Djangoで用意されています。

見た目


ログインページに、パスワードを忘れた方へのリンクが増えました。
ログインページに、パスワードを忘れた方、というボタンがついた

クリックすると、メールアドレスを入力するよう求められます。
メールアドレスを入力するよう求められます

入力して送信すると、再設定用ページのURLがメールで送付されます。
メールのぶん面はこのように

再設定ページで、パスワードを入力して、送信すると反映されます。
アクセスすると、パスワードの再設定ページ

urls.py


4つほど増えます。
    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'),


foms.py


メールアドレスを入力するMyPasswordResetFormと、再設定のためのMySetPasswordFormができました。
from django import forms
from django.contrib.auth.forms import (
    AuthenticationForm, UserCreationForm, PasswordChangeForm,
    PasswordResetForm, SetPasswordForm
)
...
...
...
class MyPasswordResetForm(PasswordResetForm):
    """パスワード忘れたときのフォーム"""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'


class MySetPasswordForm(SetPasswordForm):
    """パスワード再設定用フォーム(パスワード忘れて再設定)"""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'



login.html


ログインページにでも、パスワードを忘れた方用のリンクを作っておきましょう。
    <!-- 右側、会員登録エリア -->
    <div class="card col-md-6">
        <div class="card-body">
            <a href="{% url 'register:user_create' %}" class="btn btn-success btn-lg btn-block" >会員登録</a><hr>
            <a href="{% url 'register:password_reset' %}" class="btn btn-success btn-lg btn-block" >パスワードを忘れた方</a>
        </div>
    </div><!-- 右側、会員登録エリアおわり -->



views.py


Djangoで用意されたビューと、先程定義したフォームを使うだけです。
...
from django.contrib.auth.views import (
    LoginView, LogoutView, PasswordChangeView, PasswordChangeDoneView,
    PasswordResetView, PasswordResetDoneView, PasswordResetConfirmView, PasswordResetCompleteView
)
...
from django.urls import reverse_lazy
...
from .forms import (
    LoginForm, UserCreateForm, UserUpdateForm, MyPasswordChangeForm,
    MyPasswordResetForm, MySetPasswordForm
)
...
...
...
class PasswordReset(PasswordResetView):
    """パスワード変更用URLの送付ページ"""
    subject_template_name = 'register/mail_template/reset/subject.txt'
    email_template_name = 'register/mail_template/reset/message.txt'
    template_name = 'register/password_reset_form.html'
    form_class = MyPasswordResetForm
    success_url = reverse_lazy('register:password_reset_done')


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


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


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



mail_template/reset/subject.txt


パスワード再設定用メールの、題名です。
ほにゃらら - パスワードの再設定



mail_template/reset/message.txt


パスワード再設定用メールの本文です。各変数は、ビュー側で勝手に作ってくれています。
{{ user.username }} 様

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

再設定用URL
{{ protocol}}://{{ domain }}{% url 'register:password_reset_confirm' uid token %}

ほにゃらら



password_reset_form.html


メールアドレスを入力するページですね。好きなようにカスタマイズしてください。
{% extends "register/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-lg">送信</button>
</form>
{% endblock %}


password_reset_done.html


メール送信したよ、というページ
{% extends "register/base.html" %}
{% block content %}
<p>
    パスワード再設定用のメールを送信しました。<br>
    メールに記載されているリンクから再設定を行ってください。
</p>
{% endblock %}



password_reset_confirm.html


新しいパスワードを設定するページ。好きなようにカスタマイズしてください。
{% extends "register/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-lg">送信</button>
</form>
{% endblock %}



password_reset_complete.html


再設定したぞ、というページ
{% extends "register/base.html" %}
{% block content %}
<p>
    パスワード再設定を完了しました。<br>
    <a class="btn btn-primary btn-lg" href="{% url 'register:login' %}">ログイン</a>
</p>
{% endblock %}