-
django room detail by FBVProject using python/Cloning Airbnb 2021. 3. 6. 16:40
url 설정
config/urls.py
urlpatterns = [ path("rooms/", include("rooms.urls", namespace="room")) ]
rooms/urls.py
from django.urls import path from . import views app_name = "rooms" urlpatterns = [path("<int:pk>/", views.roomDetail, name="room-detail")]
{% url %}
room_card.html의 일부다. {% url 'rooms:room-detail' room.pk %}는 /rooms/<int:pk>/와 같다.
FBV(Function Based View)
rooms/views.py
room.host.date_joined은 dateField여서 strftime method를 가지고 있다. strftime는 python의 날짜 형식 변환 method다.
# rooms/views.py from django.shortcuts import render, redirect, reverse from django.contrib import messages from . import models def roomDetail(request, pk): room = models.Room.objects.get(pk=pk) if room is None: messages.error(request, "Room does not exsit") return redirect(reverse("core:home")) month = room.host.date_joined.strftime("%b") return render( request, "pages/rooms/room_detail.html", context={"room": room, "joined_month": month}, )
strftime의 옵션은 다음과 같다. 그 중 나는 month 값만 필요하기 때문에 %b를 인자로 줬다.
더보기◆ %d : 0을 채운 10진수 표기로 날짜를 표시
◆ %m : 0을 채운 10진수 표기로 월을 표시
◆ %y : 0을 채운 10진수 표기로 2자리 년도
◆ %Y : 0을 채운 10진수 표기로 4자리 년도
◆ %H : 0을 채운 10진수 표기로 시간 (24시간 표기)
◆ %I : 0을 채운 10진수 표기로 시간 (12시간 표기)
◆ %M : 0을 채운 10진수 표기로 분
◆ %S : 0을 채운 10진수 표기로 초
◆ %f : 0을 채운 10진수 표기로 마이크로 초 (6자리)
◆ %A : locale 요일
◆ %a : locale 요일 (단축 표기)
◆ %B : locale 월
◆ %b : locale 월 (단축 표기)
◆ %j : 0을 채운 10진수 표기로 년중 몇 번째 일인지 표시
◆ %U : 0을 채운 10진수 표기로 년중 몇 번째 주인지 표시 (일요일 시작 기준)
◆ %W : 0을 채운 10진수 표기로 년중 몇 번째 주인지 표시 (월요일 시작 기준)method
rooms/models.py
template을 만들면서 필요한 method들은 models.py에 정의했다. calculate_*() method는 review의 정의된 fields 값을 계삲ㄴ다.
class Room(core_models.TimeStampedModel): """ Room Model Definition """ def __str__(self): return self.name def get_first_photo(self): try: (photo,) = self.photos.all()[:1] return photo.file.url except Exception as error: print(error) return None def get_four_photo(self): try: photos = self.photos.all()[1:5] return enumerate(photos) except Exception as error: print(error) return None def get_review_points(self): reviews = self.reviews.all() all_ratings = 0 for review in reviews: all_ratings += review.avg if len(reviews) == 0: return 0 ratings = round(all_ratings / len(reviews), 2) return ratings def calculate_accuracy(self): reviews = self.reviews.all() if len(reviews) == 0: return 0 all_accuracy_points = 0 for review in reviews: all_accuracy_points += review.accuracy accuracy = round(all_accuracy_points / len(reviews), 2) return accuracy def calculate_communication(self): reviews = self.reviews.all() all_communication_points = 0 for review in reviews: all_communication_points += review.communication if len(reviews) == 0: return 0 communication = round(all_communication_points / len(reviews), 2) return communication def calculate_cleanliness(self): reviews = self.reviews.all() all_cleanliness_points = 0 for review in reviews: all_cleanliness_points += review.cleanliness if len(reviews) == 0: return 0 cleanliness = round(all_cleanliness_points / len(reviews), 2) return cleanliness def calculate_location(self): reviews = self.reviews.all() all_location_points = 0 for review in reviews: all_location_points += review.location if len(reviews) == 0: return 0 location = round(all_location_points / len(reviews), 2) return location def calculate_check_in(self): reviews = self.reviews.all() all_check_in_points = 0 for review in reviews: all_check_in_points += review.check_in if len(reviews) == 0: return 0 check_in = round(all_check_in_points / len(reviews), 2) return check_in def calculate_value(self): reviews = self.reviews.all() all_value_points = 0 for review in reviews: all_value_points += review.value if len(reviews) == 0: return 0 value = round(all_value_points / len(reviews), 2) return value
templates
pages/rooms/room_detail.html
{% extends 'base.html' %} {% load remainder_op %} {% block page_title %} {{room.name}} {% endblock page_title %} {% block content %} <div class="flex flex-col items-center justify-items-center"> <div class="w-10/12"> <h1 class="text-3xl font-bold">{{room.name}}</h1> <div class="flex text-gray-500 mb-5"> <div class="rating"> <i class="fas fa-star text-red-500"></i> <span class="font-bold">{{room.get_review_points}}</span> <span class=" font-normal">({{room.reviews.count}})</span> <span class="mx-1">·</span> </div> {% if room.host.superhost %} <span class="ml-1">Superhost</span> <span class="mx-2">·</span> {% endif %} <div class="font-medium"> <span class="mr-1">{{room.address}},</span> <span class="mr-1">{{room.city}},</span> <span>{{room.country.name}}</span> </div> </div> <div class="grid grid-cols-2 grid-rows-2-200px gap-2 mb-12"> {% if room.get_first_photo is not None %} <img src="{{room.get_first_photo}}" class="row-span-full rounded-tl-lg rounded-bl-lg h-full w-full bg-cover bg-center" alt="Room Photo"> {% endif %} <div class="grid grid-cols-2 grid-rows-2-200px gap-2"> {% for index, photo in room.get_four_photo %} {% remainder_op index as remainder %} <img src="{{photo.file.url}}" class="h-full w-full bg-cover bg-center {% if remainder == 1 %}rounded-tr-lg{% elif remainder == 3 %}rounded-br-lg{% endif %}" alt="Room Photo"> {% endfor %} </div> </div> <main class="room_detail"> <div class="w-3/5"> <div class="section pb-0 flex justify-between"> <div class="space__left"> <h2 class="text-2xl font-bold">{{room.room_type}} hosted by {{room.host.first_name}}</h2> <div class="mb-4"> <span>Maximum</span> <span>{{room.guests}} guests ·</span> <span>{{room.beds}} beds ·</span> <span>{{room.bedrooms}} bedrooms ·</span> <span>{{room.bathrooms}} bathrooms</span> </div> {% if user.pk == room.host.pk and request.session.is_hosting %} <button class="button"> <a href="{% url 'rooms:edit-room' room.pk %}">Edit Room</a> </button> {% endif %} </div> <div> {% include 'mixins/avatar.html' with user=room.host %} </div> </div> <div class="section flex flex-col"> <p class="content"> {{room.description}}</p> </div> <div class="section"> <h4>Amenity</h4> <ul> {% for amenity in room.amenities.all %} <li>{{amenity}}</li> {% endfor %} </ul> </div> <div class="section"> <h4>Facility</h4> <ul> {% for facility in room.facilities.all %} <li>{{facility}}</li> {% endfor %} </ul> </div> <div class="section"> <h4>House Rules</h4> <ul> {% for house_rule in room.house_rules.all %} <li>{{house_rule}}</li> {% endfor %} </ul> </div> </div> <div class="ml-10 w-1/4"> calendar </div> </main> <div class="w-3/5 grid grid-cols-2 gap-10 mr-40 border-b border-gray-300 mb-6 pb-6"> calendar </div> <div class="room_reviews"> <div class="text-lg font-bold mb-8"> <i class="fas fa-star text-red-500"></i> <span>{{room.get_review_points}} points</span> <span>({{room.reviews.count}} review{{room.reviews.count|pluralize}})</span> </div> <div class="review_points"> <div> <span>Accuracy</span> <span class="point">{{room.calculate_accuracy}}</span> </div> <div> <span>Communication</span> <span class="point">{{room.calculate_communication}}</span> </div> <div> <span>Cleanliness</span> <span class="point">{{room.calculate_cleanliness}}</span> </div> <div> <span>Location</span> <span class="point">{{room.calculate_location}}</span> </div> <div> <span>Check In</span> <span class="point">{{room.calculate_check_in}}</span> </div> <div> <span>Value</span> <span class="point">{{room.calculate_value}}</span> </div> </div> <div class="grid grid-cols-2 gap-x-52 gap-y-3 mb-10"> {% for review in room.reviews.all %} <div> <div class="flex items-center mb-4"> {% if review.user.avatar %} <a href="{% url 'users:profile' review.user.pk%}"> <img src="{{review.user.avatar.url}}" class="img w-16 h-16 mr-4" alt="User avatar" title="User Avatar"> </a> {% else %} <a href="{% url 'users:profile' review.user.pk%}"> <div class="img w-16 h-16 mr-4">{{review.user.first_name|first|upper}}</div> </a> {% endif %} <div class="flex flex-col"> <span class="font-bold">{{review.user|capfirst}}</span> <span class="text-sm text-gray-400">{{review.created}}</span> </div> </div> <p class="mb-4 font-normal">{{review.review}}</p> </div> {% endfor %} </div> </div> <div class="px-6"> <div class="host-info__left"> <div class="flex items-center mb-4"> {% if room.host.avatar %} <a href="{% url 'users:profile' room.host.pk%}"> <img src="{{room.host.avatar.url}}" class="img w-16 h-16 mr-4" alt="User avatar" title="User Avatar"> </a> {% else %} <a href="{% url 'users:profile' room.host.pk%}"> <div class="img w-16 h-16 mr-4">{{room.host.first_name|first|upper}}</div> </a> {% endif %} <div class="flex flex-col"> <h3 class="font-bold text-2xl">Host : {{room.host.first_name}}</h3> <span class="text-sm text-gray-400">Member Since: {{joined_month}}, {{room.host.date_joined.year}}</span> </div> </div> <div class="grid grid-cols-2 gap-2"> <div class="w-10/12"> <div class="flex text-base mb-8"> <div class="host-info__reviews mr-4"> <i class="fas fa-star text-red-500 mr-2"></i> <span>{{room.reviews.count}} review{{room.reviews.count|pluralize}}</span> </div> <div class="host-info__superhost"> {% if room.host.superhost %} <i class="fas fa-medal text-red-500 mr-2"></i> <span>Superhost</span> {% endif %} </div> </div> <p class="mb-4 text-lg"> {{room.description}} </p> {% if room.host.superhost %} <h3 class="font-bold text-base mb-2">{{room.host.first_name}} is superhost.</h3> <p> Superhost is a host who boasts rich experience and high rating and do their best to make their guests comfortable in their accommodation. </p> {% endif %} </div> <div class="flex flex-col"> <span class="mb-4">Response rate : 100%</span> <span class="mb-6">Response time : In one hour</span> <button class="button px-1 py-2 w-1/2"> <a href="#">Contact the host</a> </button> </div> </div> </div> </div> </div> </div> {% endblock content %}
사용자 정의 template tag
template tag를 만드는 방법은 app/templatetags/<tag_name> 을 __init__.py와 같이 생성하면 된다.
다음은 tag를 짜는 방법이다. register = template.Library와 @register.simple_tag를 사용하면 된다. 만약 template filter를 사용하고 싶다면 @register.filter를 사용하면 된다. 함수 이름은 파일 이름과 같아야 한다.
# rooms/templatetags/remainder_op.py from django import template register = template.Library() @register.simple_tag def remainder_op(index): return index % 4
template tag를 사용할 때는 {% load %}를 사용하면 된다. {% remainder_op index as remainder %}에서 remainder_op는 함수 이름이고 index는 remainder_op에게 전달하는 인자다. 그리고 as 뒤에 remainder는 remainder_op가 반환하는 값을 받는다. 즉, return index % 4의 값을 받는다. 그 값에 따라 rounded-tr-lg냐 아니면 rounded-br-lg을 class에 할당한다.
# templates/pages/rooms/room_detail.html {% load remainder_op %} {% for index, photo in room.get_four_photo %} {% remainder_op index as remainder %} <img src="{{photo.file.url}}" class="{% if remainder == 1 %} rounded-tr-lg {% elif remainder == 3 %} rounded-br-lg {% endif %}" alt="Room Photo"> {% endfor %}
참고 자료
- 노마드 코더의 Airbnb 클론 강의
- strftime
- 사용자 정의 template tag
소스 코드
github.com/zpskek/airbnb-clone-v3/commit/3f13ecc7cef45ecc51d126d9fa2f3714c84445c4
'Project using python > Cloning Airbnb' 카테고리의 다른 글
django session (0) 2021.03.06 django room detail by CBV (0) 2021.03.06 django change password by CBV (0) 2021.03.05 django change password by FBV (0) 2021.03.05 django update profile by CBV (0) 2021.03.05