/ PythonDjango

Djangoで、メールアドレス変更ページの作成

概要

Djangoで、会員登録機能を自作するシリーズの1つです。

前回はパスワード変更ページと忘れた際の再設定ページを作りました。

今回はメールアドレスの変更ページを作ります。会員登録時と同じように、メール内のリンクをクリックすると登録されるようにします。

見た目

まず、メールアドレス変更ページがあります。
メールアドレス変更ページ

送信すると、変更先のメールアドレスにメールが届きます。
メール
変更先のメールアドレスにメールが届きます

そのメール内のリンクをクリックすると、メールアドレスの変更完了です。
メールアドレスの変更完了

urls.py

3つ足します。

    path('email/change/', views.EmailChange.as_view(), name='email_change'),
    path('email/change/done/', views.EmailChangeDone.as_view(), name='email_change_done'),
    path('email/change/complete/<str:token>/', views.EmailChangeComplete.as_view(), name='email_change_complete'),

forms.py

メールアドレス変更用フォームを作ります。

class EmailChangeForm(forms.ModelForm):
    """メールアドレス変更フォーム"""

    class Meta:
        model = User
        fields = ('email',)

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

    def clean_email(self):
        email = self.cleaned_data['email']
        User.objects.filter(email=email, is_active=False).delete()
        return email

会員登録時と同じように、clean_email()では仮登録状態で、同じメールアドレスのアカウントを削除しています。

views.py

class EmailChange(LoginRequiredMixin, generic.FormView):
    """メールアドレスの変更"""
    template_name = 'register/email_change_form.html'
    form_class = EmailChangeForm

    def form_valid(self, form):
        user = self.request.user
        new_email = form.cleaned_data['email']

        # URLの送付
        current_site = get_current_site(self.request)
        domain = current_site.domain
        context = {
            'protocol': 'https' if self.request.is_secure() else 'http',
            'domain': domain,
            'token': dumps(new_email),
            'user': user,
        }

        subject_template = get_template('register/mail_template/email_change/subject.txt')
        subject = subject_template.render(context)

        message_template = get_template('register/mail_template/email_change/message.txt')
        message = message_template.render(context)
        send_mail(subject, message, None, [new_email])

        return redirect('register:email_change_done')


class EmailChangeDone(LoginRequiredMixin, generic.TemplateView):
    """メールアドレスの変更メールを送ったよ"""
    template_name = 'register/email_change_done.html'


class EmailChangeComplete(LoginRequiredMixin, generic.TemplateView):
    """リンクを踏んだ後に呼ばれるメアド変更ビュー"""
    template_name = 'register/email_change_complete.html'
    timeout_seconds = getattr(settings, 'ACTIVATION_TIMEOUT_SECONDS', 60*60*24)  # デフォルトでは1日以内

    def get(self, request, **kwargs):
        token = kwargs.get('token')
        try:
            new_email = loads(token, max_age=self.timeout_seconds)

        # 期限切れ
        except SignatureExpired:
            return HttpResponseBadRequest()

        # tokenが間違っている
        except BadSignature:
            return HttpResponseBadRequest()

        # tokenは問題なし
        else:
            User.objects.filter(email=new_email, is_active=False).delete()
            request.user.email = new_email
            request.user.save()
            return super().get(request, **kwargs)

EmailChangeビューはメールアドレスの変更ページで、送信ボタンが押されると、アドレス変更の確認メールを送ります。generic.FormViewを利用していることや、フォームのsave()メソッドを呼んでいないことに注意してくださいEmailChangeFormはモデルフォームではありますが、それはモデルのunique=True等のチェックのためにモデルフォームを利用しただけで、データを作成する必要はありません。

EmailChangeDoneは、メールアドレスの変更メールを送ったよページを表示するだけのビューです。

EmailChangeCompleteはトークンの検証を行い、問題がなければ新しいメールアドレスに変更するビューです。

base.html

メールアドレス変更のリンクを作ります。

          <li>
            <a class="nav-item nav-link" href="{% url 'register:email_change' %}">メールアドレスの変更</a>
          </li>

email_change_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 %}

email_change_done.html

確認メール見てね、と表示するだけのページです。

{% extends "register/base.html" %}
{% block content %}
<p>
    確認メールに記載されているリンクをクリックすると、アドレスが変更されます。
</p>
{% endblock %}

email_change_complete.html

無事にメールアドレスが変更されたら表示されるページです。

{% extends "register/base.html" %}
{% block content %}
<p>
    メールアドレスを変更しました。<br>
</p>
{% endblock %}

email_change/subject.txt

送信するメールの題名です。

ほにゃらら - メールアドレス変更

email_change/message.txt

送信するメールの本文です。

下記URLよりサイトにアクセスすると、メールアドレスの変更が完了します。

{{ protocol}}://{{ domain }}{% url 'register:email_change_complete' token %}

ほにゃらら