Project using python/Cloning Airbnb

django Update Profile by FBV

Cog Factory 2021. 3. 5. 09:22

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>/update-profile/", views.updateProfile, name="update-profile")]

{% url %}

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

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 import messages
from .exception import VerifyUser

@login_required
def updateProfile(request, pk):
    if request.method == "GET":
        try:
            if request.user.pk != pk:
                raise VerifyUser("Page Not found")

            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"))
            genders = models.User.GENDER_CHOICES
            languages = models.User.LANGUAGE_CHOICES
            currencies = models.User.CURRENCY_CHOICES
            login_methods = models.User.LOGIN_CHOICES

            choices = {
                "genders": genders,
                "languages": languages,
                "currencies": currencies,
                "login_methods": login_methods,
            }

            return render(
                request,
                "pages/users/update_profile.html",
                context={"user": user, **choices},
            )
        except VerifyUser as error:
            messages.error(request, error)
            return redirect("core:home")
        return render(request, "pages/users/update_profile.html", {"user": user})
    elif request.method == "POST":
        try:
            if request.user.pk != pk:
                raise VerifyUser("Page Not found")

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

            avatar = request.FILES.get("avatar")
            if avatar is not None and avatar != "":
                user.avatar = avatar

            first_name = request.POST.get("first_name")
            if first_name is not None:
                user.first_name = first_name

            last_name = request.POST.get("last_name")
            if last_name is not None:
                user.last_name = last_name

            email = request.POST.get("email")
            if email is not None:
                user.email = email

            gender = request.POST.get("gender")
            if gender is not None:
                user.gender = gender

            language = request.POST.get("language")
            if language is not None:
                user.language = language

            currency = request.POST.get("currency")
            if currency is not None:
                user.currency = currency

            birthdate = request.POST.get("birthdate")

            if birthdate is not None:
                user.birthdate = birthdate

            superhost = bool(request.POST.get("superhost"))
            if superhost is not None:
                user.superhost = superhost

            bio = request.POST.get("bio")
            if bio is not None:
                user.bio = bio

            user.save()
            messages.success(request, f"{user.email} profile update succeded")
            return redirect(reverse("users:profile", kwargs={"pk": pk}))
        except VerifyUser as error:
            messages.error(request, error)
            return redirect("core:home")

templates

pages/users/update_profile.html

  <form> tag에 enctype="multipart/form-data" attribute가 있다. django가 file을 업로드할 때 사용하는 속성이다. views.py에서는 request.FILES.get()으로 업로드하는 file에 접근할 수 있다.

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

{% extends 'base.html' %}


{% block page_title %}
  Edit {{user.first_name}}'s Profile
{% endblock page_title %}

{% block search-bar %}
<div></div>
{% endblock search-bar %}

{% block content %}
  <div class="background">
    <div class="wrap">
      <div class="flex flex-col items-center justify-center">
        <div class="mb-4">
          {% include 'mixins/avatar.html' with user=user %}
        </div>
        {% if user.login_method == "github" %}
          <div class="flex items-center mb-4">
            <h3 class="font-bold text-base mr-2">Github Email:</h3>
            <span>{{user.email}}</span>
          </div>
        {% elif user.login_method == "kakao" %}
          <div class="flex items-center mb-4">
            <h3 class="font-bold text-base mr-2">Kakao Email:</h3>
            <span>{{user.email}}</span>
          </div>
        {% endif %}
      </div>
      <form method="post" class="form" enctype="multipart/form-data">
        {% csrf_token %}
        <div class="form_input rounded-tl-lg rounded-tr-lg">
          <label for="first_name">First Name</label>
          <input type="text" id="first_name" placeholder="First Name" value="{{user.first_name}}" name="first_name">
        </div>

        <div class="form_input">
          <label for="last_name">Last Name</label>
          <input type="text" id="last_name" placeholder="Last Name" value="{{user.last_name}}" name="last_name">
        </div>

          {% if user.login_method == "email" %}
            <div class="form_input">
              <label for="email">Email</label>
              <input type="text" id="email" placeholder="Email" value="{{user.email}}" name="email">
            </div>
          {% endif %}

        {% if user.login_method == "email" %}
          <div class="form_input">
            <label for="avatar">Avatar</label>
            <input type="file" id="avatar" name="avatar" accept="image/*">
          </div>
        {% endif %}

        <div class="select">
          <label for="gender">Gender</label>
          <select name="gender" id="gender">
            {% for key, value in genders %}
              <option value="{{value}}" {% if value == user.gender %}selected{% endif %}>
                {{value}}
              </option>
            {% endfor %}
          </select>
        </div>

        <div class="select">
          <label for="language">Language</label>
          <select name="language" id="language">
            {% for key, value in languages %}
              <option value="{{value}}" {% if value == user.language %}selected{% endif %}>
                {{value}}
              </option>
            {% endfor %}
          </select>
        </div>
        
        <div class="select">
          <label for="currency">Currency</label>
          <select name="currency" id="currency">
            {% for key, value in currencies %}
              <option value="{{value}}" {% if value == user.currency %}selected{% endif %}>
                {{value}}
              </option>
            {% endfor %}
          </select>
        </div>

        <div class="birthdate">
          <label for="birthdate">Birthdate</label>
          <input type="date" id="birthdate" name="birthdate" placeholder="Birthdate" value="{{user.birthdate|date:'Y-m-d'}}" required>
        </div>

        <div class="form_input rounded-br-lg rounded-bl-lg">
          <textarea id="bio" placeholder="Bio" name="bio">{{user.bio}}</textarea>
        </div>
        <button class="form_button">Edit Profile</button>
      </form>
      {% if user.login_method == 'email' %}
        <button class="button bg-red-500">
          <a href="#">Change Password</a>
        </button>
      {% endif %}
      <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/c3862c66c02bf1bdac912cb592768d9536584777