-
django room detail by CBVProject using python/Cloning Airbnb 2021. 3. 6. 17:14
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>/와 같다.
CBV(Class Based View)
rooms/views.py
CBV는 django에서 정의한 여러가지 View들을 이용한다. 그 중 DetailView를 이용해서 Room detail을 작성한다. 여기서 반드시 필요한 attribute는 model이다.
- model : page를 구성할 main model이다.
- template_name : rendering할 template이다.
- def get_context_data() : "room" 이외에도 추가로 client에 context를 만들 수 있다.
room.host.date_joined은 dateField여서 strftime method를 가지고 있다. strftime는 python의 날짜 형식 변환 method다.
# rooms/views.py from django.views.generic import DetailView from . import models class RoomDetailView(DetailView): model = models.Room template_name = "pages/rooms/room_detail.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) room = context["room"] month = room.host.date_joined.strftime("%b") context["joined_month"] = month return context
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 클론 강의
- DetailView
- strftime
- 사용자 정의 template tag
소스 코드
github.com/zpskek/airbnb-clone-v3/commit/7e6fc393f2eb518084ae70322a227d1985a7b0cd
'Project using python > Cloning Airbnb' 카테고리의 다른 글
django translator (0) 2021.03.07 django session (0) 2021.03.06 django room detail by FBV (0) 2021.03.06 django change password by CBV (0) 2021.03.05 django change password by FBV (0) 2021.03.05