2020-03-30

Django 14. 비밀번호 변경 구현

Django 내장폼인 PasswordChangeForm을 사용해 사용자의 비밀번호 변경을 구현합니다.


1. forms.py 작성

비밀번호 변경 템플릿에 적용할 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.pyurlpatterns에 아래의 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. 결과

django-project-14

*전체 html, css 등은 자세하게 포스팅하지 않습니다. 제 Github에서 소스를 확인하실 수 있습니다.