[Django] User CRUD (회원 가입, 회원 탈퇴, 회원정보 수정, 비밀번호 변경)

문지은·2023년 4월 30일
0

Django 기초

목록 보기
12/14
post-thumbnail

Django User obejct에 대해 이해하고 User CRUD 기능(회원 가입, 회원 탈퇴, 회원정보 수정, 비밀번호 변경)을 구현해보자!


⭐️ 회원가입

  • 회원가입은 User를 Create하는 것이며 UserCreationForm built-in form을 사용

UserCreationForm

  • 주어진 username과 password로 권한이 없는 새 user를 생성하는 ModelForm
  • class Meta: model = User가 등록된 form
class UserCreationForm(forms.ModelForm):
	...
	class Meta:
			model = User
			fields = ("username",)
			field_classes = {"username":, UsernameField}
  • 우리는 User model을 Custom 하였으므로 Custom한 User 모델을 사용하기 위해서는 반드시 form도 Custom 하여야 한다.
    • 그렇지 않으면 AttributeError 발생

UserCreationForm() 커스텀

# accounts/forms.py

from django.contrib.auth import get_user_model
from django.contrib.auth.forms import UserCreationForm, UserChangeForm

class CustomUserCreationForm(UserCreationForm):

    class Meta(UserCreationForm.Meta):
        model = get_user_model()

class CustomUserChangeForm(UserChangeForm):
    
    class Meta(UserChangeForm.Meta):
        model = get_user_model()

get_user_model()

  • 현재 프로젝트에서 활성화된 사용자 모델(active user model)을 반환
  • 직접 참조하지 않는 이유
    • 예를 들어 기존 User 모델이 아닌 User 모델을 커스텀한 상황에서는 커스텀 User 모델을 자동으로 반환해주기 때문
  • Django는 User 클래스를 직접 참조하는 대신 get_user_model()을 사용해 참조해야 한다고 강조하고 있음

회원가입 페이지 작성

url

# accounts/urls.py

from django.urls import path
from . import views

app_name = 'accounts'
urlpatterns = [
    ...,
    path('signup/', views.signup, name='signup'),
]

view

# accounts/views.py

from .forms import CustomUserCreationForm

def signup(request):
    if request.method == 'POST':
        form = CustomUserCreationForm(request.POST)
        if form.is_valid():
            user = form.save()
            return redirect('articles:index')
    else:
        form = CustomUserCreationForm()
    context = {
        'form':form
    }
    return render(request, 'accounts/signup.html', context)

회원 가입 페이지 링크 작성

<!-- base.html -->

<div id="nav">
      <a href="{% url 'accounts:signup' %}">회원가입</a>
      <a href="{% url 'accounts:login' %}">로그인</a>
      <a href="{% url 'accounts:logout' %}">로그아웃</a>
    </div>
    <h3 id="user-hello"><i>안녕하세요, {{user}} 님 !</i></h3>
    <hr>
    <div id="content">
      {% block content %}{% endblock content %}
    </div>

회원가입 후 페이지 / 테이블 확인

회원가입 후 바로 로그인 되게 하기

# accounts/views.py

def signup(request):
    if request.method == 'POST':
        form = CustomUserCreationForm(request.POST)
        if form.is_valid():
            user = form.save()
            auth_login(request, user) # 회원 가입 하면 자동 로그인
            return redirect('articles:index')
    else:
        form = CustomUserCreationForm()
    context = {
        'form':form
    }
    return render(request, 'accounts/signup.html', context)

⭐️ 회원 탈퇴

  • 회원 탈퇴 하는 것은 DB에서 유저를 Delete 하는 것

회원 탈퇴 로직 작성

url

# accounts/urls.py

from django.urls import path
from . import views

app_name = 'accounts'
urlpatterns = [
    ...,
    path('delete/', views.delete, name='delete'),
]

view

# accounts/views.py

# url(/accounts/delete/)로 접근해도 삭제 가능
def delete(request):
    user = request.user
    user.delete()
    return redirect('articles:index')

# 코드 수정
def delete(request):
    if request.method == 'POST':
        user = request.user
        user.delete()
        auth_logout(request)
        return redirect('articles:index')
    else:
        return redirect('articles:index')

회원탈퇴 링크 작성

<!-- base.html -->

...

<form action="{% url 'accounts:delete' %}" method="POST">
  {% csrf_token %}
  <input type="submit" value="회원탈퇴">
</form>

...

탈퇴하면서 해당 유저의 세션 정보도 함께 지우기

  • 탈퇴 후 로그아웃의 순서가 바뀌면 안됨
    • 먼저 로그아웃 해버리면 해당 요청 객체 정보가 없어지기 때문에 탈퇴에 필요한 정보 또한 없어지기 때문
# accounts/views.py

def delete(request):
    user = request.user
    user.delete()
    auth_logout(request)
    return redirect('articles:index')

회원 탈퇴 후 페이지 / 테이블 확인

⭐️ 회원 정보 수정

UserChangeForm

  • 사용자의 정보 및 권한을 변경하기 위해 admin 인터페이스에서 사용되는 ModelForm
  • UserChangeForm 또한 ModelForm 이기 때문에 instance 인자로 기존 user 데이터 정보를 받는 구조 또한 동일함
  • 이미 이전에 CustomUserChangeForm으로 확장했기 때문에 CustomUserChangeForm 사용

UserChangeForm 사용시 문제점

  • 일반 사용자가 접근해서는 안 될 정보들(fields)까지 모두 수정이 가능해짐
    • admin 인터페이스에서 사용되는 ModelForm이기 때문
  • 따라서 UserChangeForm을 상속받아 작성해 두었던 서브클래스 CustomUserChangeForm에서 접근 가능한 필드를 조정해야 함

CustomUserChangeForm fields 재정의

# accounts/forms.py

class CustomUserChangeForm(UserChangeForm):

    class Meta(UserChangeForm.Meta):
        model = get_user_model()
        fields = ('email', 'first_name', 'last_name',)

회원 정보 수정 페이지 작성

url

# accounts/urls.py

from django.urls import path
from . import views

app_name = 'accounts'
urlpatterns = [
    ...,
    path('update/', views.update, name='update'),
]

view

# accounts/views.py
from .forms import CustomUserChangeForm

def update(request):
    if request.method == 'POST':
        form = CustomUserChangeForm(request.POST, instance=request.user)
        if form.is_valid():
            form.save()
            return redirect('articles:index')
    else:
        form = CustomUserChangeForm(instance=request.user)
    context = {
        'form':form
    }
    return render(request, 'accounts/update.html', context)

template

<!-- accounts/update.html -->

{% extends 'base.html' %}

{% block content %}
    <h1>회원정보수정</h1>

    <form action="{% url 'accounts:update' %}" method="POST">
        {% csrf_token %}
        {{form.as_p}}
        <input type="submit">
    </form>
{% endblock content %}

회원정보 수정 페이지 링크 작성

<!-- base.html -->

<div id="nav">
      <a href="{% url 'accounts:signup' %}">회원가입</a>
      <a href="{% url 'accounts:login' %}">로그인</a>
      <a href="{% url 'accounts:logout' %}">로그아웃</a>
      <a href="{% url 'accounts:update' %}">회원정보 수정</a>
      <form action="{% url 'accounts:delete' %}" method="POST">
        {% csrf_token %}
        <input type="submit" value="회원탈퇴">
      </form>
    </div>

회원 수정 페이지 / 수정 후 테이블 확인

⭐️ 비밀번호 변경

  • PasswordChangeForm 사용

PasswordChangeForm

  • 사용자가 비밀번호를 변경할 수 있도록 하는 Form
  • 이전 비밀번호를 입력하여 비밀번호를 변경할 수 있도록 함
  • 이전 비밀번호를 입력하지 않고 비밀번호를 설정할 수 있는 SetPasswordForm을 상속받는 서브 클래스

비밀번호 변경 페이지 작성

url

# accounts/urls.py

from django.urls import path
from . import views

app_name = 'accounts'
urlpatterns = [
    ...,
    path('password/', views.change_password, name='change_password'),
]

view

# accounts/views.py

from django.contrib.auth.forms import PasswordChangeForm

def change_password(request):
    if request.method == 'POST':
        form = PasswordChangeForm(request.user, request.POST)
        if form.is_valid():
            form.save()
            return redirect('articles.index')
    else:
        form = PasswordChangeForm(request.user)
    context = {
        'form':form,
    }
    return render(request, 'account/change_password.html', context)

template

<!-- accounts/change_password.html -->

{% extends 'base.html' %}

{% block content %}
    <h1>비밀번호 변경</h1>

    <form action="{% url 'accounts:change_password' %}" method="POST">
        {% csrf_token %}
        {{form.as_p}}
        <input type="submit" value="변경하기">
    </form>

{% endblock content %}

암호 변경 시 세션 무효화 방지하기

  • 비밀번호가 변경되면 기존 세션과의 회원 인증 정보가 일치하지 않게 되어 버려 로그인 상태가 유지되지 못함
  • 비밀번호는 잘 변경되었으나 비밀번호가 변경되면서 기존 세션과의 회원 인증 정보가 일치하지 않기 때문
update_session_auth_hash
  • update_session_auth_hash()
  • 현재 요청(current request)과 새 session data가 파생 될 업데이트된 사용자 객체를 가져오고, sesison data를 적절하게 업데이트 해줌
  • 암호가 변경되어도 로그아웃 되지 않도록 새로운 password의 session data로 session을 업데이트
  • update_session_auth_hash 작성
# accounts/views.py
from django.contrib.auth import update_session_auth_hash

def change_password(request):
    if request.method == 'POST':
        form = PasswordChangeForm(request.user, request.POST)
        if form.is_valid():
            form.save()
            # 암호가 변경되어도 로그아웃 되지 않음
            update_session_auth_hash(request, form.user)
            return redirect('articles.index')
    else:
        form = PasswordChangeForm(request.user)
    context = {
        'form':form,
    }
    return render(request, 'accounts/change_password.html', context)

📍 완성된 프로젝트 전체 코드

https://github.com/mjieun0956/TIL/tree/master/Django/230323

profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

0개의 댓글