2020-03-30
Django 14. 비밀번호 변경 구현
Django 내장폼인 PasswordChangeForm을 사용해 사용자의 비밀번호 변경을 구현합니다.
비밀번호 변경 템플릿에 적용할 form을 생성하기 위해 forms.py 에 django 내장폼인 PasswordChangeForm 을 상속받는 폼클래스를 아래와 같이 입력합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # users/forms.py from django.contrib.auth.forms import PasswordChangeForm class CustomPasswordChangeForm(PasswordChangeForm): def __init__(self, *args, **kwargs): super(CustomPasswordChangeForm, self).__init__(*args, **kwargs) self.fields['old_password'].label = '기존 비밀번호' self.fields['old_password'].widget.attrs.update({ 'class': 'form-control', 'autofocus': False, }) self.fields['new_password1'].label = '새 비밀번호' self.fields['new_password1'].widget.attrs.update({ 'class': 'form-control', }) self.fields['new_password2'].label = '새 비밀번호 확인' self.fields['new_password2'].widget.attrs.update({ 'class': 'form-control', })
이전에 포스팅한 비밀번호 찾기 후 비밀번호 변경 폼과의 차이점은 상속받는 Django form에 있습니다. 비밀번호 찾기에서 사용한 SetPasswordForm 은 기존의 비밀번호를 입력받지 않고 비밀번호 변경이 가능 하고, 이번 포스팅에서 구현한 PasswordChangeForm 은 기존의 비밀번호를 입력해야한다 는 차이점이 있습니다. 따라서 공식문서를 살펴보면 PasswordChangeForm은 SetPasswordForm을 상속받아 구현되어있는것을 알 수 있습니다.
Django 내장폼 공식문서
2. views.py 작성 views.py 에 비밀번호 수정 view를 아래와 같이 입력합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # users/views.py from .forms import CustomPasswordChangeForm from django.contrib.auth import update_session_auth_hash @login_message_required def password_edit_view(request): if request.method == 'POST': password_change_form = CustomPasswordChangeForm(request.user, request.POST) if password_change_form.is_valid(): user = password_change_form.save() update_session_auth_hash(request, user) messages.success(request, "비밀번호를 성공적으로 변경하였습니다.") return redirect('users:profile') else: password_change_form = CustomPasswordChangeForm(request.user) return render(request, 'users/profile_password.html', {'password_change_form':password_change_form})
비밀번호 변경 창에서 GET 요청이 들어오면 request.user 로 사용자의 정보를 방금 생성한 CustomPasswordChagneForm 에 담아 render합니다. 사용자는 변경할 비밀번호를 Submit하여 POST 요청을 하게 되고 is_valid검사를 통과하면 비밀번호를 변경하고 message와 함께 profile창으로 redirect되게 됩니다.
비밀번호가 변경되었을 시 기존 session에 담긴 사용자의 비밀번호가 일치하지 않아 session이 해제되는 것을 막기 위해 update_session_auth_hash 를 import하여 추가합니다. 사용자는 비밀번호를 변경하더라도 request로 session에 비밀번호를 자동으로 업데이트 해주는 update_session_auth_hash 덕분에 세션 유지가 가능하게 됩니다.
3. urls.py 작성 생성한 비밀번호 view를 연결하기 위해 urls.py 의 urlpatterns 에 아래의 path를 추가합니다.
1 2 3 # users/urls.py path('profile/password/', views.password_edit_view, name='password_edit'),
4. templates.py 작성 비밀번호 변경 창의 templates는 아래와 같습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 <!-- templates/users/profile_password.html --> <form method="POST" novalidate> {% csrf_token %} <div> <label name="label_old_password" for="{{ password_change_form.old_password.id_for_label }}">{{ password_change_form.old_password.label }}</label> {{ password_change_form.old_password }} <script type="text/javascript"> if (document.getElementsByName('old_password')[0].value != '') { document.getElementsByName('label_old_password')[0].setAttribute('class', 'active'); } </script> {% if password_change_form.old_password.errors %} <script type="text/javascript"> document.getElementsByName('old_password')[0].setAttribute('class', 'form-control is-invalid') </script> {% for error in password_change_form.old_password.errors %} <div class="invalid-feedback"> {{ error }} </div> {% endfor %} {% endif %} </div> <div> <label name="label_new_password1" for="{{ password_change_form.new_password1.id_for_label }}">{{ password_change_form.new_password1.label }}</label> {{ password_change_form.new_password1 }} <script type="text/javascript"> if (document.getElementsByName('new_password1')[0].value != '') { document.getElementsByName('label_new_password1')[0].setAttribute('class', 'active'); } </script> {% if password_change_form.new_password1.errors %} <script type="text/javascript"> document.getElementsByName('new_password1')[0].setAttribute('class', 'form-control is-invalid') </script> {% for error in password_change_form.new_password1.errors %} <div class="invalid-feedback"> {{ error }} </div> {% endfor %} {% endif %} </div> <div> <label name="label_new_password2" for="{{ password_change_form.new_password2.id_for_label }}">{{ password_change_form.new_password2.label }}</label> {{ password_change_form.new_password2 }} <script type="text/javascript"> if (document.getElementsByName('new_password2')[0].value != '') { document.getElementsByName('label_new_password2')[0].setAttribute('class', 'active'); } </script> {% if password_change_form.new_password2.errors %} <script type="text/javascript"> document.getElementsByName('new_password2')[0].setAttribute('class', 'form-control is-invalid') </script> {% for error in password_change_form.new_password2.errors %} <div style="white-space: nowrap;" class="invalid-feedback"> {{ error }} </div> {% endfor %} {% endif %} <small> 최소 8개의 숫자와 문자 또는 특수기호를 입력해주세요. </small> </div> </form>
5. 결과
*전체 html, css 등은 자세하게 포스팅하지 않습니다. 제 Github 에서 소스를 확인하실 수 있습니다.