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 %}
참고 자료
- 노마드 코더의 Airbnb 클론 강의
- file upload
- {% csrf_token %}
소스 코드
github.com/zpskek/airbnb-clone-v3/commit/c3862c66c02bf1bdac912cb592768d9536584777