Project using python/Cloning Airbnb

django update profile by CBV

Cog Factory 2021. 3. 5. 10:06

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.UpdateProfileView.as_view(),
        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/과 같다.

CBV(Class Based View)

users/views.py

  CBV는 django에서 정의한 여러가지 View들을 이용한다. 그 중 UpdateView를 이용해서 update profile page를 작성한다. UpdateView는 FormView와는 다르게 forms.py를 작성하지 않고도 View 안에서 fields를 정의할 수 있다. 아니 정의해야만 rendering이 가능하다.

  get_form method를 이용해서 form의 속성을 제어할 수 있다.

from django.views.generic import UpdateView
from . import mixins


class UpdateProfileView(mixins.LoginOnlyView, mixins.EmailLoginOnlyView, UpdateView):
    model = models.User
    fields = {
        "avatar",
        "first_name",
        "last_name",
        "email",
        "gender",
        "language",
        "currency",
        "birthdate",
        "superhost",
        "bio",
    }
    template_name = "pages/users/update_profile.html"
    success_message = "Profile Updated"

    def get_form(self, form_class=None):
        form = super().get_form(form_class=form_class)
        form.fields["email"].widget.attrs = {"placeholder": "Email"}
        form.fields["first_name"].widget.attrs = {"placeholder": "First name"}
        form.fields["last_name"].widget.attrs = {"placeholder": "Last name"}
        form.fields["bio"].widget.attrs = {"placeholder": "Bio"}
        return form

users/models.py

  UpdateView는 user가 profile을 성공적으로 update 했을 때 redirect가 될 url이 필요하다. models.py에서 get_absolute_url() method를 만들면, 해당 url로 redirect가 된다. redirect 대신 reverse를 사용한 이유는 kwargs 인자를 전달하기 위해서다. kwargs로 pk 값을 전달해야 하는데, redirect는 이것이 불가능 하다.

    def get_absolute_url(self):
        return reverse("users:profile", kwargs={"pk": self.pk})

users/mixins.py

  UpdateView가 상속하는 mixins다. mixins는 Route 보안을 담당한다.

from django.contrib.auth.mixins import UserPassesTestMixin, LoginRequiredMixin
from django.contrib import messages
from django.urls import reverse_lazy
from django.shortcuts import redirect, reverse


class LoginOnlyView(LoginRequiredMixin):
    login_url = reverse_lazy("users:login")


class EmailLoginOnlyView(UserPassesTestMixin):

    permission_denied_message = "Page Not Found"

    def test_func(self):
        return self.request.user.login_method == "email"

    def handle_no_permission(self):
        messages.error(self.request, "Can't go there")
        return redirect(reverse("core:home"))

templates

pages/users/update_profile.html

  코드 길이는 달라지지 않았다. 물론 {{form}} 한 줄로 <form><form/> 안을 압축할 수 있지만 제어를 하기 위해서 일일이 {{form.*}}로 했다.

{% 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>
          {{form.first_name}}
        </div>

        <div class="form_input">
          <label for="last_name">Last Name</label>
          {{form.last_name}}
        </div>

          {% if user.login_method == "email" %}
            <div class="form_input">
              <label for="email">Email</label>
              {{form.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>
          {{form.gender}}
        </div>

        <div class="select">
          <label for="language">Language</label>
          {{form.language}}
        </div>
        
        <div class="select">
          <label for="currency">Currency</label>
          {{form.currency}}
        </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 %}
  

참고 자료

  • 노마드 코더의 Airbnb 클론 강의
  • UpdateView

소스 코드

github.com/zpskek/airbnb-clone-v3/commit/f35e672a49555e4a0c5f9c037d34391bb3406bdf