Django

회원기능 구현하기

zhelddustmq 2024. 8. 19. 00:36

01. 회원가입

02. 회원탈퇴

03. 정보 수정

04. 비밀번호변경

 

 

01. 회원가입

  • Django는 기본 auth.User를 가지고 있기에 이를 기반으로한 기본적인 회원가입 ModelForm을 제공
  • UserCreationForm
    • username과 password 로 새로운 user를 생성하는 ModelForm
    • username, password1, password2를 가짐
#  (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)
#(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

02. 회원탈퇴

# 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"),
]

 

# 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")

탈퇴하고 → 세션지우고. 이 순서가 바뀌면 안됨

 

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

 

 

03. 정보 수정

# (update) accounts/urls.py

from django.urls import path
from . import views

app_name = "accounts"
urlpatterns = [
		...
    path("update/", views.update, name="update"),
]
# (update) acounts/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)
# 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 %}
#  (update) base.html

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

유저가 수정하면 안될 정보까지 다 보임

→ 필요한 기능이 있지만 내가 커스텀 하고 싶다면?

→ 상속을 통한 Form 커스텀

 

CustomUserChangeForm 만들기

 

# (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 = # 적어야하는데 모르겠단말이지
  • get_user_model()
    • 현재 프로젝트에서 활성화 되어있는 유저모델을 반환
    • 직접 User 모델을 import 할 수 있지만 get_user_model 을 사용하기를 권장
    • django는 다중 User 모델을 지원하므로 확장에 용이
    • 프로젝트의 유연성과 확장성을 높여줌
# (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 완성시키기

# (update) 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)

 

 

04. 비밀번호변경

 

 

PasswordChangeForm

  • 유저의 비밀번호를 변경할 수 있는 Form을 제공
# (password) urls.py

from django.urls import path
from . import views

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

from django.contrib.auth.forms import (
    ...
    PasswordChangeForm,
)

@login_required
@require_http_methods(["GET", "POST"])
def change_password(request):
    form = PasswordChangeForm(request.user)
    context = {"form": form}
    return render(request, "accounts/change_password.html", context)
# change_password.html

{% 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

 

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

 

Using the Django authentication system | Django documentation

The web framework for perfectionists with deadlines.

docs.djangoproject.com

저거 쓰라네요.

 

# (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)

'Django' 카테고리의 다른 글

Admin  (0) 2024.08.19
Django Static, Media(runserver)  (1) 2024.08.19
Auth  (0) 2024.08.17
URL Namespace / Templates Namespace (Django 플젝 시작 시 유의사항)  (0) 2024.08.16
Django Form, Django Model Form  (0) 2024.08.16