이번에는 회원탈퇴 구현하기(1) 과는 다르게, 비밀번호를 통한 본인인증 후에 회원탈퇴를 진행하게끔 구현해줄 것이다.
※ forms.py 수정하기
forms.py를 다음과 같이 수정한다:
# forms.py
from django import forms
from django.contrib.auth.hasers import check_password
class PasswordCheckForm(forms.Form):
password = forms.Charfield(label='비밀번호', widget=forms.PasswordInput())
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
self.user = user
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
user_password = self.user.password
if password:
if check_password(password, user_password) == False:
self.add_error('password', '비밀번호가 틀렸습니다.')
비밀번호를 확인해주는 PasswordCheckForm을 만들어준다. forms.Form을 상속하는 이유는 다른 모델들과 상호작용하는 것이 아니기 때문에 Modelform이 아닌 forms.Form을 사용한다.
password=forms.Charfield(label='비밀번호', widget=forms.PasswordInput()) 에서는 label은 비밀번호로 (밑에 html template에서 다시 설명하겠다.), widget은 HTML input 변수를 정의하는 Django의 방법이다 (이 또한 밑의 html template에서 다시 설명하겠다.)
def __init__을 통해 password와 같은 field를 통과시킨다. 또한, user를 정의함으로써, 사용자도 또한 통과시켜준다.
def clean(self)을 정의하는데, 여기서 cleaned_data=super().clean()은 form으로 들어온 값을 읽어들이는 역할을 하고, password=cleaned_data.get('password') 를 통해 form으로 들어온 값 중 'password'를 다시 password 라는 변수에 저장한다. user_password는 사용자의 비밀번호로 정의한다. (cleaned_data=super().clean() 코드를 쓰지 않고 cleaned_data를 사용해도 상관 없으나, 정확히 정의해주기 위해 쓰면 더 좋다.)
마지막으로 만약 비밀번호가 입력된 것이라면, django.contrib.auth.hashers에서 import한 check_password 메소드를 통해 사용자의 비밀번호와 입력된 비밀번호가 같은지 확인해주고, 아니라면 비밀번호가 틀렸다는 에러를 password 필드에 추가해준다.
add_error()를 통해, cleaned_data에 있는 password를 제거해줌으로써, 다시 읽었을 때 생기는 충돌을 방지할 수 있다.
※ views.py 수정하기
# views.py
from .forms import PasswordCheckForm
def deleteUser(request):
form = PasswordCheckForm(request.user)
if request.method == 'POST':
form = PasswordCheckForm(request.user, request.POST)
if form.is_valid():
request.user.delete()
logout(request)
return redirect('login')
context = {'form':form}
return render(request, 'accounts/delete_user.html', context)
우선 form이라는 변수에 PasswordCheckForm(request.user)로 정의함으로써 비밀번호 인증을 위한 사용자를 저장시켜준다.
만약 request 방식이 POST 였다면, form에 request.POST 변수도 통과시켜준다.
만약 이 form이 유효한 값이라면, 사용자를 삭제시켜주고 다시 login 페이지로 되돌려준다. (else를 정의안 한 이유는 단지 시작에 form=PasswordCheckForm(request.user) 를 통과시켜주고 시작했기에 상관 없기 때문이다.)
마지막으로 값들을 delete_user.html로 render 시켜준다.
※ urls.py / delete_user.html 구현하기
다음은 urls.py 이다
# urls.py
urlpatterns = [
...
path('delete_user/', views.deleteUser, name='delete_user'),
...
]
다음은 accounts/delete_user.html 이다:
<!-- accounts/delete_user.html -->
<form method="POST" novalidate>
{% csrf_token %}
{{ user.user_id }} 회원님의 계정이 삭제됩니다
<div>
<label name="label_password" for="{{ form.password.id_for_label }}">{{ form.password.label }}</label>
{{ form.password }}
{% if form.password.errors %}
{% for error in form.password.errors %}
{{ error }}
{% endfor %}
{% endif %}
</div>
<button type="submit" name="bt">탈퇴하기</button>
</form>
※ 주의: 여기서 button을 눌렀을 때 탈퇴하기를 구현하려면, AJAX를 이용해야 하나, 여기선 단순히 보여주기 위해 버튼만 구현했다. 만약 버튼을 디자인하거나 하지 않을 것이라면, 단순히 <input type="submit"...> 식으로 구현해도 된다. 아래에서 보여주는 예제에서는 <input type="submit"...> 으로 구현했다.
여기서 method="POST" 옆에 novalidate이라 적혀져 있는 것을 볼 수 있는데, 이는 만약 입력되지 않았을 경우, 입력되지 않았다는 메세지를 띄우게끔 해주는 것이다.
이후 여기서 label name="label_password" for="{{ form.password.id_for_label }}"이 있는데, 여기서의 for 값과 form에 있는 id 값이 같으면 자동적으로 연결되게 한다. 따라서 id_for_label은 이 form.password 의 id 값을 불러오게끔 하여, CheckPasswordForm과 연결되게 끔 설정한 것이다. 또한, 이 label tag는 이 label이 클릭되면 연결된 양식에 입력이 가능하게끔 하는 역할을 한다.
{{ form.password.label }}은 forms.py에 설정해놨던 label="비밀번호" 를 불러와준다.
※ Widget과 Label 조금 더 자세히 보기
이전에 예시를 보자:
# forms.py
password = forms.CharField(label="비밀번호", widget=forms.PasswordInput())
# accounts/delete_user.html
<label name="label_password" for="{{ form.password.id_for_label }}">{{ form.password.label }}</label>
여기서 label tag를 누르게 된다면 연결된 양식에 입력이 가능하게끔 하는 역할을 한다고 했다. 그렇다면 이 for은 무슨 역할을 하는걸까? 위의 예시를 조금 바꾸어보았다:
# forms.py
password = forms.CharField(label="비밀번호", widget=forms.PasswordInput(attrs={'id':'password_widget'}))
# accounts/delete_user.html
<label name="label_password" for="password_widget">{{ form.password.label }}</label>
이렇게 해도 label tag를 눌렀을 때, 입력 박스가 자동으로 활성화되는 것을 볼 수 있다. 첫번째 예시에서 form.password.id_for_label은 자동적으로 해당 password의 label을 위한 id를 입력해주었기 때문에 활성화가 된다. 하지만 만약 for의 값이나, attrs에 정의된 id의 값이 다르다면, 눌러지지 않는다:
# forms.py
password = forms.CharField(label="비밀번호", widget=forms.PasswordInput(attrs={'id':'different_id'}))
# accounts/delete_user.html
<label name="label_password" for="password_widget">{{ form.password.label }}</label>
여기서 이 label은 password_widget으로 설정되어 있는 반면, forms.py에 정의된 password 의 id는 'different_id'로 정의되어 있기 때문에 label을 클릭해도 비밀번호 입력 박스가 활성화 되지 않는다.
예시)
{{ form.password.label }} 부분인 '비밀번호'를 누르게 된다면,
이렇게 입력 박스가 활성화 되는 것이다.
※ 예제 보기
다음으로는 이를 구현한 매우 ugly한 html template을 보자:
이 Delete User를 누르게 된다면, /delete_user/ 로 넘어가게 된다:
이제 여기에 제대로된 비밀번호를 입력한다면 바로 로그인 페이지로 넘어가지만, 만약 다른 비밀번호를 입력하였을 경우, 다음과 같이 나타난다:
(앞서 말했듯이, 입력 박스 옆의 '비밀번호'를 누른다면 입력박스가 활성화되면서 입력이 가능해진다)
'Django 공부하기' 카테고리의 다른 글
<Django 공부하기> Password Change 구현하기 - (1) (0) | 2020.11.25 |
---|---|
<Django 공부하기> @property decorator 알아보기 (0) | 2020.11.18 |
<Django 공부하기> templates 폴더를 Root Directory에 정의하기 (templates 폴더를 apps 폴더 안에서 찾게 하지 않기) (0) | 2020.11.13 |
<Django 공부하기> Customizing Admin Page (1) - Customizing Users Page (0) | 2020.11.13 |
<Django 공부하기> 회원탈퇴 구현하기 (1) - 단순 회원탈퇴 (No Password Needed) (0) | 2020.11.12 |