Djangoアプリケーションにメール認証パスワードリセット機能を追加する

django.contrib.auth を使って既存の Django アプリにユーザーのGメールを使ったパスワードリセット機能を実装する手順を記します。私自身これを実装するのにあちこちのサイトを漁ったので備忘録的にまとめておきます。

## はじめに
Djangoユーザー認証系の実装を調べるとよくあるのが、ユーザー認証用に「accounts」などの名前で新たにアプリケーションを立ち上げてそっちに実装していくというものですが、今回は既存のアプリケーションに直接組み込みます。

既にログイン機能が備わっているという前提で進めていきます。

ここで使う既存のアプリケーションはmyappという名前です。

### settings.pyの設定
メール送信に必要な情報を入れる。

アプリケーションを代表するGmailアドレス（EMAIL_HOST_USER）とそのパスワード（EMAIL_HOST_PASSWORD）は自分用に書き換えて設定する。

~~~python:settings.py
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_HOST_USER = 'myapp@gmail.com'
EMAIL_HOST_PASSWORD = 'gmail-password'
EMAIL_USE_TLS = True
~~~

### URL の設定
myapp/urls.pyに以下のコードを追加する。

~~~python:myapp/urls.py
from django.urls import path
:
(省略)
:
from . import passwords_reset #ビューファイルの追加（ファイルはこの後作成）

app_name = 'myapp'
urlpatterns = [
    :
    (省略)
    :
    path('password_reset/', passwords_reset.PasswordReset.as_view(), name='password_reset'), #追加
    path('password_reset_done/', passwords_reset.PasswordResetDone.as_view(), name='password_reset_done'), #追加
    path('password_reset_confirm/<uidb64>/<token>/', passwords_reset.PasswordResetConfirm.as_view(), name='password_reset_confirm'), #追加
    path('password_reset_complete/', passwords_reset.PasswordResetComplete.as_view(), name='password_reset_complete'), #追加
]
~~~

### ビューの設定
password_reset.py という新しく作ったファイルに次のビュー関数に設定する。

django.contrib.auth.views という Django がもともと持っているライブラリを使えば、パスワード設定関連の機能は特に関数を設定しなくても実装できる。

これはhtmlファイルも用意してくれるものだが、今回はフロントの部分だけは自分でカスタマイズしてバックの処理はこのライブラリに頼る。

~~~python:password_reset.py
from django.shortcuts import render
from django.contrib.auth.views import PasswordResetView, PasswordResetDoneView, PasswordResetConfirmView, PasswordResetCompleteView
from django.urls import reverse_lazy

class PasswordReset(PasswordResetView):
    """パスワード変更用URLの送付ページ"""
    subject_template_name = 'mail/subject.txt'
    email_template_name = 'mail/message.txt'
    template_name = 'password_reset_form.html'
    success_url = reverse_lazy('myapp:password_reset_done')

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

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

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

### HTMLファイルを作成
絶対に必要な部分だけ載せる。

~~~html:templates/password_reset.html
(省略)
:
<form action="" method="POST">
    {{ form.non_field_errors }}
    <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 %}
    <br/>
    <div class="form-group row">
      <div class="col-6">
        <button type="submit" class="btn btn-primary btn-block">送信</button>
      </div>
    </div>
</form>
:
(省略)
~~~
~~~html:templates/password_reset_done.html
<p>パスワード再設定メールを送信しました。
~~~
~~~html:templates/password_reset_confirm.html
<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>
~~~
~~~html:templates/password_reset_complete.html
<p>パスワード再設定を完了しました。
  <a class="btn btn-primary btn-block" href="{% url 'accounts:index' %}">ログイン画面へ</a>
</p>
~~~

### メール内容を設定

~~~:templetes/mail/subject.txt
パスワード再設定
~~~
~~~:templetes/mail/message.txt
{{ user.username }} 様

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

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

## 動作確認
これでURL「localhost:8000/password_reset」を踏むとそのままパスワードリセットができるようになる。

ファイル指定のパスや、実際にファイルを置いている場所などに間違いが無いかを確認する。

メールが届いてパスワードリセットができれば成功！

## 参考資料
https://qiita.com/t-iguchi/items/4c76a10458786e115ce3