Lina's Toolbox

Django로 회원가입 기능 구현하기 본문

스파르타 내일 배움 캠프 AI 웹개발 과정/Django framework

Django로 회원가입 기능 구현하기

Woolina 2024. 9. 11. 18:30

 

 

Django의 Auth System을 이용해서 기본적인 회원기능을 구현 해보자!

 

회원가입

 

구현하기

(signup) accounts/forms.py

from django import forms


class ArticleForm(forms.Form):
    title = forms.CharField(max_length=10)
    content = forms.CharField()

 

(signup) accounts/views.py

from django.contrib.auth.forms import UserCreationForm

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

post일경우는 바인딩 폼 만든다. (request.POST) 채워줌

 

(signup) accounts/templates/accounts/signup.html

{% extends "base.html" %}

{% block content %}
    <h1>회원가입</h1>
    <form action="{% url "accounts:signup" %}" method="POST">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">회원가입</button>
    </form>
{% endblock %}

 

(signup) base.html

<a href="{% url 'accounts:signup'%}">회원가입</a>

 

 

가입 및 편의성 업데이트

  • 가입하고 바로 로그인까지 되면 좋을 것 같아요!
    • 로그인? → 세션 생성해주기 → django가 해주던데 → login()

(session) accounts/views.py

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

accounts/views.py

request.user

→ 장고가 알아서, request객체 안의 쿠키에서 sid 빼서, 세션 테이블에서 비교해서, 우리 실제 user테이블에 있는 user 객체를 할당해줌

 

매우편함!


회원탈퇴

 💡 게시글을 지우는 것과 크게 다를 것이 없습니다. 마치 게시글을 지우듯이 DB에서 User를 지워주면 됩니다!

 

구현하기

1. accounts/urls.py

from django.urls import path
from . import views

app_name = "accounts"
urlpatterns = [
    path("login/", views.login, name="login"),
    path("logout/", views.logout, name="logout"),
    path("signup/", views.signup, name="signup"),
    path("delete/", views.delete, name="delete"),
]

 

 

2. accounts/views.py

@require_POST
def delete(request):
    if request.user.is_authenticated:
        request.user.delete()
    return redirect("index")
  • 그런데 회원탈퇴했는데도 쿠키에 세션 아이디와, 세션 테이블에 아직 남아있다?
  • 만약 탈퇴하면서 해당 유저의 세션도 지우고 싶다면?
  • logout()을 사용하면 되겠죠?
@require_POST
def delete(request):
    if request.user.is_authenticated:
        request.user.delete()
        auth_logout(request)
    return redirect("index")

탈퇴하고 → 세션지우고. 이 순서가 바뀌면 안됩니다!

  • 어차피 회원 탈퇴 후 남아있는 저 세션 아이디로 받은 값은, 어차피 저걸로 유저를 찾아도 해당 유저가 없기 때문에 의미는 없다.
  • 그래도..깔끔하게 세션까지 삭제하자! 
  • →  auth_logout

 

 

3. base.html 에 회원 탈퇴버튼 추가

(delete) base.html

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


회원 정보 수정

  • 기본 User Model에 대한 기본적인 수정 ModelForm 제공합니다.
  • UserChangeForm
    • 기본 User Model에 대한 Form을 제공합니다.

구현하기

  1. accounts/urls.py
from django.urls import path
from . import views

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

 

 

2. accounts/views.py (개요)

from django.views.decorators.http import require_http_methods
from django.contrib.auth.forms import (
    ...
    UserChangeForm,
)

...
@require_http_methods(["GET", "POST"])
def update(request):
    if request.method == "POST":
        pass
    else:
        form = UserChangeForm(instance=request.user)
    context = {"form": form}
    return render(request, "accounts/update.html", context)

 

 

3. accounts/templates/accounts/update.html

{% extends "base.html" %}

{% block content %}
    <h1>회원정보수정</h1>
    
    <form action="{% url 'accounts:update' %}" method="POST">
        {{ form.as_p }}
        {% csrf_token %}
        <button type="submit">수정하기</button>
    </form>

{% endblock %}

 

 

4. base.html (버튼만들기)

(update) base.html

<a href="{% url "accounts:update" %}">회원정보수정</a>

💡 의도한 대로 되긴 되는데 … 유저가 수정하면 안될 정보까지 다 보이네요
→ 필요한 기능이 있지만 내가 커스텀 하고 싶다면?
→ 상속을 통한 Form 커스텀 👍

 

 

CustomUserChangeForm 만들기

accounts/forms.py

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


class CustomUserChangeForm(UserChangeForm):
    class Meta:
        model = get_user_model()
        fields = # 적어야하는데 모르겠단말이지

 

  • get_user_model()
    • 현재 프로젝트에서 활성화 되어있는 유저모델을 반환합니다.
    • 직접 User 모델을 import 할 수 있지만 get_user_model 을 사용하기를 권장하고 있습니다!
    • django는 다중 User 모델을 지원하므로 확장에 용이합니다.
    • 프로젝트의 유연성과 확장성을 높여줍니다.
🥸  우리가 작성한 forms.py 내부의 fields에는 어떤 값들이 들어갈까요?

 

  • UserChangeForm

https://github.com/django/django/blob/944745afe2ec45aed30cef799c250107f1364ca7/django/contrib/auth/forms.py#L256

 

django/django/contrib/auth/forms.py at 944745afe2ec45aed30cef799c250107f1364ca7 · django/django

The Web framework for perfectionists with deadlines. - django/django

github.com

 

 

User Model

https://github.com/django/django/blob/944745afe2ec45aed30cef799c250107f1364ca7/django/contrib/auth/models.py#L406

 

 

AbstractUser

https://github.com/django/django/blob/944745afe2ec45aed30cef799c250107f1364ca7/django/contrib/auth/models.py#L335

→ 아 ~ 여기있네요!

fields에는 보시는 것처럼 first_name, email… 등의 필드들이 들어갈 수 있습니다!

 

→ 공식문서에도 잘나와있습니다!

(https://docs.djangoproject.com/en/4.2/ref/contrib/auth/#user-model)

 

(update) accounts/forms.py

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


class CustomUserChangeForm(UserChangeForm):
    class Meta:
        model = get_user_model()
        fields = (
            "username",
            "email",
            "first_name",
            "last_name",
        )

 

 

accounts/views.py 완성시키기

@require_http_methods(["GET", "POST"])
def update(request):
    if request.method == "POST":
        form = CustomUserChangeForm(request.POST, instance=request.user)
        if form.is_valid():
            form.save()
            return redirect("index")
    else:
        form = CustomUserChangeForm(instance=request.user)
    context = {"form": form}
    return render(request, "accounts/update.html", context)

비밀번호변경

 

PasswordChangeForm

  • 유저의 비밀번호를 변경할 수 있는 Form을 제공

구현하기

1. (password)urls

from django.urls import path
from . import views

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

 

 

2. (password)views (1차)

{% extends "base.html" %}

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

<form action="{% url 'accounts:change_password' %}" method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <button type="submit">비밀번호 변경</button>
</form>
{% endblock content %}

잘 나오는 것 같네요!

그런데, 회원정보수정의 링크를 보면 …

  • 유저가 바꾸면 안되는 정보까지도 수정할 수 있게 뜬다.
  • 패스워드 변경 후 돌아가는 페이지도 우리가 원하는 경로로 커스텀하고 싶다 ! /accounts/password/
  • → 오버라이딩(Overriding)

 

  • CustomUserUpdateForm
    • 상속받고 있는 기존 UserChangeForm을 살펴봅시다.

아, 이렇게 되어있어서 나오는거구나

오버라이딩해서 수정하기

(password) CustomUserChangeForm

class CustomUserChangeForm(UserChangeForm):

    class Meta:
        model = get_user_model()
        fields = (
            "username",
            "email",
            "first_name",
            "last_name",
        )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.fields.get("password"):
            password_help_text = (
                "You can change the password " '<a href="{}">here</a>.'
            ).format(f"{reverse('accounts:change_password')}")
            self.fields["password"].help_text = password_help_text



UserChangeForm이것을 상속받아서 커스텀해보자.

다른 건 다 괜찮고, 필드가 들어있는 Meta 클래스만 바꾸면 된다.

 

from django.contrib.auth.models import user

하고 model = User을 해도 되지만,

장고에서는 앱마다 다른 유저, 즉 멀티유저 시스템을 제공한다.

그래서 다른 방법을 권장하고 있다.

→ 현재 활성화된 유저를 가져오는 방법    get_user_model()

 

  firstname, lastname, email으로만 오버라이딩 해줬는데, 패스워드도 보인다!

  그이유는, UserChangeForm 에서, pw는 Meta의 field가 아닌 form에서 따로 지정해주기 때문.

  안보여주고 싶으면 password자체를 오버라이딩해주면된다. (password = None)

 

__init__을 오버라이드 해주면,

부모의 __init__이 먼저 실행 된후,

우리가 오버라이딩한 __init__이 실행됨.

 

reverse: url_name으로부터 url를 찾아가는것

(url_name줄테니까 너가 이거보고 찾아서 알아서 url 넣어줘!)

 

views 완성하기

(password) views (2차)

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("index")
    else:
        form = PasswordChangeForm(request.user)
    context = {"form": form}
    return render(request, "accounts/change_password.html", context)

 

 

 

그런데 비밀번호를 한 번 변경해보면 … !

비밀번호를 변경하면 기존 세션의 인증 정보와 일치하지 않기때문에 로그인이 풀리게 됩니다.

이러한 동작이 다소 불편할 수 있기 때문에 공식문서를 찾아보고 한 번 변경해봅시다!

✔️ 공식문서가 답입니다 여러분
https://docs.djangoproject.com/en/4.2/topics/auth/default/#session-invalidation-on-password-change

 

 

완성

(password) views (완성)

@login_required
@require_http_methods(["GET", "POST"])
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("index")
    else:
        form = PasswordChangeForm(request.user)
    context = {"form": form}
    return render(request, "accounts/change_password.html", context)

회원정보 수정해서 비밀번호를 바꾸면

쿠키에 있는 세션아이디로 접근하는 유저의 정보와, 비밀번호가 변경된 후의 정보가 일치하지 않아 로그아웃이 된다.

update_session_auth_hash → 하면 로그인 유지된다.

관리자도구에서 확인해보면 세션아이디는 새로 바뀐것이 확인됨.

'스파르타 내일 배움 캠프 AI 웹개발 과정 > Django framework' 카테고리의 다른 글

Django Admin  (1) 2024.09.12
Django Static & Media  (0) 2024.09.12
Pydantic란? 사용법 / Serializer와 비교  (0) 2024.09.10
Redis  (0) 2024.09.10
배포하기  (0) 2024.09.04