Project using python/Cloning Airbnb

django change password by FBV

Cog Factory 2021. 3. 5. 14:08

url 설정

config/urls.py

urlpatterns = [
    path("users", include("users.urls", namespace="users")),
]

users/urls.py

from django.urls import path
from . import views

app_name = "users"

urlpatterns = [path("<int:pk>/change-password/", views.change_password, name="change-password")]

{% url %}

  update_profile.html의 일부다. Change Password button을 누르면 password를 수정할 수 있는 page로 간다. url template tag를 이용해서 update_profile page로 갈 수 있게 한다. {% url 'users:change-password' user.pk %}는 /users/<int:pk>/change-password/와 같다.

FBV(Function Based View)

users/views.py

  FBV에서는 if 문으로 GET method 요청이 왔을 때와 POST method 요청이 왔을 때를 구분한다. GET은 page를 보여주고 POST는 client로부터 온 data를 처리한다.

# users/views.py

from django.shortcuts import render, redirect, reverse
from django.contrib.auth import authenticate, login
from django.contrib import messages
from .exception import VerifyUser

class EmailLoggedInOnly(Exception):
    pass


@login_required
def change_password(request, pk):
    if request.method == "GET":
        try:
            if request.user.login_method != "email":
                raise EmailLoggedInOnly("Page not found 404")
            if request.user.pk != pk:
                raise VerifyUser("Page Not found 404")
            user = models.User.objects.get_or_none(pk=pk)
            if user is None:
                messages.error(request, "User does not exist")
                return redirect(reverse("core:home"))
            return render(
                request,
                "pages/users/change_password.html",
                context={"user": user},
            )
        except VerifyUser as error:
            messages.error(request, error)
            return redirect("core:home")
        except EmailLoggedInOnly as error:
            messages.error(request, error)
            return redirect("core:home")
    elif request.method == "POST":
        try:
            if request.user.login_method != "email":
                raise EmailLoggedInOnly("Page not found 404")
            if request.user.pk != pk:
                raise VerifyUser("Page Not found 404")
            user = models.User.objects.get_or_none(pk=pk)
            if user is None:
                messages.error(request, "User does not exist")
                return redirect(reverse("core:home"))
            old_password = request.POST.get("current_password")
            new_password = request.POST.get("new_password")
            new_password1 = request.POST.get("verify_password")
            user = authenticate(request, username=user.email, password=old_password)
            if user is None:
                raise ChangePasswordException("Current password is wrong!")

            if new_password != new_password1:
                raise ChangePasswordException("New password doesn't match")
            user.set_password(new_password)
            user.save()
            messages.success(request, f"{user.email}'password changed successfully")
            login(request, user)
            return redirect(reverse("users:profile", kwargs={"pk": pk}))
        except ChangePasswordException as error:
            messages.error(request, error)
            return redirect(reverse("core:home"))
        except VerifyUser as error:
            messages.error(request, error)
            return redirect("core:home")
        except EmailLoggedInOnly as error:
            messages.error(request, error)
            return redirect("core:home")

templates

pages/users/change_password.html

  {% csrf_token %}은 csrf 공격을 막기 위한 token이다. django는 보안이 뛰어나기 때문에 {% csrf_token %} tag를 사용하지 않으면 submit이 안 된다. csrf 공격의 보안은 절대 client를 신뢰하지 않는 것으로 시작한다. 즉, 이 token 값을 가지지 않은 브라우저 요청은 받아들이지 않는다.

{% extends 'base.html' %}


{% block page_title %}
  Change {{user.first_name}}'s Password
{% endblock page_title %}
  
{% block search-bar %}
<div></div>
{% endblock search-bar %}

{% block content %}
  <div class="background">
    <div class="wrap">
      <h1 class="font-bold text-xl mb-6">Change password of {{user.email}}</h1>
      <form method="post" class="form">
        {% csrf_token %}
        <div class="form_input rounded-tl-lg rounded-tr-lg">
          <input type="password" name="current_password" id="current_password" placeholder="Your current password">
        </div>

        <div class="form_input">
          <input type="password" name="new_password" id="new_password" placeholder="New password">
        </div>

        <div class="form_input rounded-bl-lg rounded-br-lg">
          <input type="password" name="verify_password" id="verify_password" placeholder="Verify your new password">
        </div>
        <button class="form_button bg-red-500">Change Password</button>
      </form>
      <button class="button bg-gray-700">
        <a href="{% url 'users:profile' user.pk %}">Back</a>
      </button>
    </div>
  </div>

{% endblock content %}
  

 

참고 자료

소스 코드

github.com/zpskek/airbnb-clone-v3/commit/6f1a9c78929d793322dd95f979b4cba93325c6fc