【django】効果的なパスワード変更画面の実装

djangoでパスワード変更画面の実装を紹介します。

「django パスワード 変更」処理でググって他の人のも見てみましたが、どれもdjangoの特性というかdjango本来が用意している正しい関数を使用しての処理を書いていないので、自分が一番シンプルでわかりやすいコードを書いてみました。

環境

環境

・python 3.8

・django 2.2

目指すゴール

昨今のモダンなパスワード変更としては、以下の3点を使用することが多いです。

  • 現在のパスワード
  • 新しいパスワード
  • 新しいパスワード(確認)

今回のゴールも上記の3点を必須事項としてコードを書いていきたいと思います。

そもそもまだ新規アカウント登録のコードを書いてないよ!!って人は以下でコードを紹介しています。

forms.pyでの処理

先ずはforms.pyでの処理を最初に書いていきます。

ユーザーオブジェクトに関してはdjango内部で用意されているget_user_modelを使用して進めて行きます。

以下にforms及びバリデーション処理もまとめて書いていきます。

注意点はコードの下にリストで書き出します。

forms.py

from django import forms
from django.core.exceptions import ValidationError

# ログイン
from django.contrib.auth import authenticate, get_user_model

User = get_user_model()

class ChangePasswordForm(forms.Form):
    """パスワード変更"""
    current_password = forms.CharField(
        required=True,
        max_length=255,
        min_length=6,
        widget=forms.PasswordInput(),
    )
    new_password = forms.CharField(
        required=True,
        max_length=255,
        min_length=6,
        widget=forms.PasswordInput(),
    )
    confirm_new_password = forms.CharField(
        required=True,
        max_length=255,
        min_length=6,
        widget=forms.PasswordInput(),
    )
    def __init__(self, user_id, *args, **kwargs):
        self.user_id = user_id
        super().__init__(*args, **kwargs)

    def clean_current_password(self):
        current_password = self.cleaned_data['current_password']
        user = User.objects.get(id=self.user_id)
        if user.username and current_password:
            auth_result = authenticate(
                username = user.username,
                password=current_password,
            )
            if not auth_result:
                raise ValidationError('現在のパスワードが間違っています。')
        return current_password

    def clean_new_password(self):
        new_password = self.cleaned_data['new_password']
        return new_password

    def clean_confirm_new_password(self):
        confirm_new_password = self.cleaned_data['confirm_new_password']
        return confirm_new_password

    def clean(self):
        cleaned_data = super().clean()
        new_password = self.cleaned_data.get('new_password')
        confirm_new_password = self.cleaned_data.get('confirm_new_password')
        if new_password != confirm_new_password:
            self.add_error(
                field='confirm_new_password',
                error=ValidationError('パスワードが一致しません。'))
        return cleaned_data
  1. 29行目の第二引数のuser_idはviews側から引数として受け取ります。views側で説明します
  2. 36行目で現在のパスワードが正しいか否かの認証をしています。authenticateメソッドは引数に、usernameとpasswordの2つを取るため、わざわざuserオブジェクトからusernameを取ってきます。39行目で今回入力されたcurrent_password(現在のパスワード)を照合に使用します
  3. 41行目でauth_resultの認証が正しいか否かの結果を判定するメソッドを書いています
  4. 53行目のcleanメソッドで、new_passwordとconfirm_new_passwordで入力された値がイコールになっているかを判定しています
  5. 58行目のadd_errorでconfirm_new_passwordのfieldに、errorを排出するようなコードを書いています


views.pyでの処理

viewes.pyでの処理を書いていきます。

views.py

from .forms import ChangePasswordForm
from django.contrib.auth import authenticate, get_user_model
User = get_user_model()

def change_password(request):
    """パスワード変更"""
    user = User.objects.get(id=request.user.id)
    if request.method == 'GET':
        form = ChangePasswordForm(user_id=request.user.id)
    elif request.method == 'POST':
        form = ChangePasswordForm(user_id=request.user.id, data=request.POST)
        if form.is_valid():
            confirm_new_password = form.cleaned_data['confirm_new_password']
            user.set_password(confirm_new_password)
            user.save()
            user = authenticate(
                username=user.username,
                password=confirm_new_password,)
            if user is not None:
                login(request, user)
            else:
                raise RuntimeError('invalid')
            return HttpResponseRedirect(reverse('sample:change_success'))
    context = {
        'form': form,
    }
    return render(request, 'sample/change_password.html', context)
  1. 先のformsでも紹介した通り、ChangePasswordForm()の引数に、request.user.idを渡します
  2. 13行目のset_password()メソッドはdjangoで用意されているメソッドでUserモデルにパスワードをsetするときにhash化して渡します
  3. 15行目でauthenticateを使用していますが、14行目のsaveでログアウト状態となりますので、再び認証してログインをしています

htmlでの処理

最後にhtml側でのフロントコードを書いていきます。

sample.html

<form action="" method="post">
    {% csrf_token %}
    <div>
        <h2>パスワード変更</h2>
        <dl>
            <div>
                <dt>現在のパスワード</dt>
                <dt>{{ form.current_password }}{{ form.current_password.errors }}</dt>
            </div>
            <div>
                <dt>新しいパスワード</dt>
                <dt>{{ form.new_password }}{{ form.new_password.errors }}</dt>
                <p class="opacity">英数字を含む6文字以上の入力をお願いします。</p>
            </div>
            <div>
                <dt>新しいパスワード(確認)</dt>
                <dt>{{ form.confirm_new_password }}{{ form.confirm_new_password.errors }}</dt>
                <p class="opacity">上記パスワードと同様のパスワードの入力をお願いします。</p>
            </div>
            <div>
                <button type="submit">変更する</button>
            </div>
        </dl>
</form>
</div>
  1. 2行目の{% csrf_token %}はformのmethodがpostの時には必須のコードになります。postのような重い処理は重要な機密情報をCRUDする時に使用されますが、csrf_tokenを使用することで、処理を暗号化して用意にセキュリティホールにならないようにしています

以上が、全貌になります。


最後に

いかがでしたでしょうか。

以上が、「【django】効果的なパスワード変更画面の実装」の紹介記事になります。

是非、あなたの制作中のwebサイトに適用してみてください。

プログラミング学習を効率良く進めるには…

私ヒロヤンがプログラミングを始めた頃は以下のような感じでした。

何から手をつけていいのか、わからない。

プログラミングを始めてみたけど、エラーが解決できない。

そしてネットで調べていくうちに膨大な時間が過ぎていきました。

私ヒロヤンの実体験より、プログラミングを効率的に学ぶために大切なことは以下のことだと考えています。

1. いつまでもダラダラとやらないで、目標を決定して短期集中する

2. マンツーマンで、わからない箇所は直ぐに質問をして即レスをもらう

.proでは私ヒロヤンが学習してきたプログラミング経験0からのpython/django、その他webサイト・サービス開発のコースが用意されています。

カウンセリング自体は無料なので話を聞いてみるだけでもいかがでしょうか?

また以下のリンク先ではdjangoを教えてくれるスクールをまとめ紹介しています。

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です