ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • django room detail by CBV
    Project 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">&centerdot;</span>
                    </div>
                    {% if room.host.superhost %}
                        <span class="ml-1">Superhost</span>
                        <span class="mx-2">&centerdot;</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 &centerdot;</span>
                                    <span>{{room.beds}} beds &centerdot;</span>
                                    <span>{{room.bedrooms}} bedrooms &centerdot;</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">&emsp;{{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 %}

     

    참고 자료

    소스 코드

    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

    댓글

Designed by Tistory.