12.-1 검색 - 창 구성

조재훈·2022년 7월 26일
0

Clone_Airbnb

목록 보기
27/31
post-thumbnail

에어비앤비에 있는 숙소 검색창

1) 도시 검색

어떤 방들이 있는지 검색해보려 한다.
rooms - views.py

def search(request):
    print(request)

templates - rooms - search.html

{% extends "base.html" %}


{% block title %}
    Search
{% endblock title %}


{% block content %}
    <h2>Search!</h2>
{% endblock content %}

rooms - urls.py

urlpatterns = [path("search/", views.search, name="search"),]

url은 127.0.0.1:800/rooms/search/ 로 한다. 세부 url이 rooms를 타고 들어오기 때문에 rooms를 붙여주어야 한다.

에어비앤비 홈피에서 보면 검색창이 header에 위치한다. header 파일을 수정해주자.
templates - partials - header.html

<header>
    <a href="{% url 'core:home' %}">Hairbnb</a>
    <ul>
        <li><a href="#">Login</a></li>
    </ul>
    <form>
        <input placeholder="Search by City">
    </form>
</header>


자 이제 도시를 검색해보면

아무데도 아닌데로 간다

header파일을 고쳐보자

...

    <form method="get" action="{% url 'rooms:search' %}">
        <input name="city" placeholder="Search by City">
    </form>

이제 검색해보면 search 페이지로는 간다.

그러면 그 검색어의 도시들은 어떻게 가져올 것인가?
views.py에서 search로 print(request)를 해놓으면 콘솔에 아래와 같이 뜬다.

한번 더 들어가서 print(request.GET)을 해보자.

QueryDict가 나왔다. 이것을 get을 이용하여 city 키워드를 추출하자.
print(request.GET.get("city")

검색창에 입력한 도시 이름이 출력되었다. 이런 방식으로 해당 문자열을 받아올 수 있다.
rooms - views.py

def search(request):
    city = request.GET.get("city")
    city = str.capitalize(city)
    return render(request, "rooms/search.html", context={"city":city})

templates - rooms - search.html

...

{% block content %}
    <h2>Search!</h2>

    <h4>Searching by {{city}}</h4>
{% endblock content %}

예압

근데 저기 검색창에 우리가 입력한 글자가 그대로 떠있으면 좋겠다. header.html에 있던

을 긁어다가 다른데로 옮기자. 그리고 여기 있는 는 로 바꾼다.
templates - partils - header.html

<a href="{% url 'core:home' %}">Hairbnb</a>
<ul>
    <li><a href="#">Login</a></li>
</ul>

templates - base.html도 잘 고쳐준다.

<body>

  <header>
    {% include "partials/header.html" %}
    {% block search-bar %}
      <form method="get" action="{% url 'rooms:search' %}">
        <input name="city" placeholder="Search by City">
      </form>
    {% endblock search-bar %}
  </header>

여기서 트릭이 하나 있는데 검색창을 search-bar 블록에 가둬놓았기 때문에 만약 검색 결과창에서 이 블록에 다른 내용을 첨가하면 그거에 맞게 내용이 바뀌게 된다. 블록 문구만 추가하고 다른 내용을 안넣으면 해당 칸이 사라지게 된다.
templates - rooms - search.html

{% block search-bar %}
{% endblock search-bar %}

그러고서 검색해보면 이렇게 검색창이 사라지는 것을 볼 수 있다.

아무튼 이런 방식을 통해서 검색 결과 페이지에서 search-bar를 따로 꾸며보고 싶은 것이다.
templates - rooms - search.html

{% block search-bar %}
    <form method="get" action="{% url 'rooms:search' %}">
        <input value="{{city}}", name="city" placeholder="Search by City">
    </form>
{% endblock search-bar %}

이제 검색 화면에서도 글자가 그대로 남아있다.

여기서도 장고버전 차이인지 인강이랑 달라지는 부분이 있다. 인강에서는 빈 칸을 그대로 검색하면 오류가 떠서 초기값을 지정해줘야하는데 여기서는 안그런다. 일단 강의에서 하라는대로 request에서 키워드를 가져올 때 초기값?을 넣어준다. 내 버전에서는 아래와 같이 한다고 해서 빈 칸으로 검색한다고 Anywhere가 뜨지는 않는다.
rooms - views.py

  ...
  
def search(request):
    city = request.GET.get("city", "Anywhere")
    city = str.capitalize(city)
    return render(request, "rooms/search.html", {"city":city})

2) 국가 검색

Django-countries에서 국가명을 가져오는 방법을 확인해보자.
https://github.com/SmileyChris/django-countries

views에서 countries 객체를 출력해보면 콘솔에 아래와 같이 나온다. countries도 context변수로 넘겨준다.

def search(request):
    city = request.GET.get("city")
    city = str.capitalize(city)
    print(countries)
    return render(
        request,
        "rooms/search.html",
        context={
            "city": city,
            "countries": countries,
        },
    )


dir(countries)를 출력해보면 아래와 같다.

name이 있으므로 이것을 갖다가 쓸 것이다.
templates - rooms - search.html

  ...
  
        <div>
            <label for="country">Country</label>
            <select>
                {% for country in countries %}
                    <option>{{country.name}}</option>
                {% endfor %}
            </select>
        </div>
        <button>Search</button>
    </form>

  ...

국가 목록이 잘 나타난다.

일단 request.GET에서 넘어오는 것을 확인해보자. 어떤 키워드들이 있는지.

def search(request):
    print(request.GET)
  ...

아직 city밖에 안뜬다.

templates를 조금 고쳐주자. select에 id와 name을 추가하고 country를 입력해준다.

        <div>
            <label for="country">Country</label>
            <select id="country", name="country">
                {% for country in countries %}
                    <option>{{country.name}}</option>
                {% endfor %}
            </select>
        </div>

이제 도시와 국가가 모두 잘 뜬다.


근데 우리는 국가명 전체가 아니라 코드가 뜨기를 바란다. 그래서 option에 value를 country.code로 추가할 것이다.

        <div>
            <label for="country">Country</label>
            <select id="country", name="country">
                {% for country in countries %}
                    <option value="{{country.code}}">{{country.name}}</option>
                {% endfor %}
            </select>
        </div>

그럼 이제 잘 뜬다.

3) 객실 타입

room type에 대한 변수를 가져오고 context 변수로도 넘겨준다.

def search(request):
    city = request.GET.get("city")
    city = str.capitalize(city)
    room_types = models.RoomType.objects.all()
    return render(
        request,
        "rooms/search.html",
        context={
            "city": city,
            "countries": countries,
            "room_types": room_types,
        },
    )

templates - rooms - search.html

        <div>
            <label for="room_type">Room Type</label>
            <select id="room_type", name="room_type">
                {% for room_type in room_types %}
                    <option value="{{room_type.pk}}">{{room_type.name}}</option>
                {% endfor %}
            </select>
        </div>
        <button>Search</button>
    </form>
{% endblock search-bar %}



URL에는 잘 드는데 검색창은 내가 선택한게 아니라 초기화되었다.
현재는 검색을 하면 아래와 같은 키워드들이 쿼리셋에 입력된다.

views 파일에 코드를 추가해서 URL에 있는 keyword들을 가져오는 변수를 context변수에 추가해주자.
rooms - views.py

def search(request):
  city = request.GET.get("city")
  city = str.capitalize(city)
  country = request.GET.get('country", "KR")
  room_types = models.RoomType.objects.all()
  room_type = request.GET.get("room_type")
  return render(
      request,
      "rooms/search.html",
      context={
          "city": city,
          "countries": countries,
          "country": country,
          "room_types": room_types,
          "room_type": room_type,
      }

코드가 헷갈리니 form과 choices로 구분을 해주자. request.GET으로 받는 것들은 form으로 들어갈 것이다.

def search(request):
  city = request.GET.get("city")
  city = str.capitalize(city)
  s_country = request.GET.get("country", "KR")
  room_types = models.RoomType.objects.all()
  s_room_type = request.GET.get("room_type")
  
  form = {
      "city": city,
      "s_country": country,
      "s_room_type": room_type,
  }
  
  choices = {
      "countries": countries,
      "room_types": room_types,
  }
  
  return render(
      request,
      "rooms/search.html",
      context={**form, **choices}
  )

** ← 이거는 안에 있는 것들을 풀어주는 역할을 한다고 한다.
이제 우리가 고른 국가 코드가 현재 국가 코드와 같은지 비교하는 내용을 추가할 것이다.
templates - rooms - search.html

{% block content %}
    <h2>Search!</h2>

    <form method="get" action="{% url 'rooms:search' %}">
        <div>
            <label for="city">City</label>
            <input value="{{city}}", id="city", name="city" placeholder="Search by City">
        </div>

        <div>
            <label for="country">Country</label>
            <select id="country" name="country">
                {% for country in countries %}
                    <option value="{{country.code}}" {% if country.code == s_country %}selected{% endif %}>{{country.name}}</option>
                {% endfor %}
            </select>
        </div>

        <div>
            <label for="room_type">Room Type</label>
            <select id="room_type" name="room_type">
                <option value="0" {% if s_room_type == 0 %}selected{% endif %}>Any kind</option>
                {% for room_type in room_types %}
                    <option value="{{room_type.pk}}" {% if s_room_type == room_type.pk %}selected{% endif %}>{{room_type.name}}</option>
                {% endfor %}
            </select>
        </div>
        <button>Search</button>
    </form>
{% endblock content %}

이제 잘 고정되어있다.


4) Price & Spaces

이제 다른 항목들도 검색창으로 추가해보자.

templates - rooms - search.html

  ...
  
        <div>
            <label for="price">Price</label>
            <input type="number", id="price", name="price" placeholder="Price">
        </div>
        
        <button>Search</button>


검색하면 URL에는 표시가 되지만 그대로 남아있지는 않다.

다른 항목들을 추가한 뒤 value도 추가해줘서 값이 남아있게 하자.
templates - rooms - search.html

        <div>
            <label for="price">Price</label>
            <input value="{{price}}", type="number", id="price", name="price" placeholder="Price">
        </div>

        <div>
            <label for="guests">Guests</label>
            <input value="{{guests}}", type="number", id="guests", name="guests" placeholder="Guests">
        </div>

        <div>
            <label for="bedrooms">Bedrooms</label>
            <input value="{{bedrooms}}", type="number", id="bedrooms", name="bedrooms" placeholder="Bedrooms">
        </div>

        <div>
            <label for="beds">Beds</label>
            <input value="{{beds}}", type="number", id="beds", name="beds" placeholder="Beds">
        </div>

        <div>
            <label for="baths">Baths</label>
            <input value="{{baths}}", type="number", id="baths", name="baths" placeholder="Baths">
        </div>
        
        <button>Search</button>

rooms - views.py

def search(request):
    print(request.GET)
    city = request.GET.get("city")
    city = str.capitalize(city)
    country = request.GET.get("country", "KR")
    room_types = models.RoomType.objects.all()
    room_type = int(request.GET.get("room_type"))
    price = int(request.GET.get("price", 0))
    guests = int(request.GET.get("guests", 0))
    bedrooms = int(request.GET.get("bedrooms", 0))
    beds = int(request.GET.get("beds", 0))
    baths = int(request.GET.get("baths", 0))

    form = {
        "city": city,
        "s_country": country,
        "s_room_type": room_type,
        "price": price,
        "guests": guests,
        "bedrooms": bedrooms,
        "beds": beds,
        "baths": baths,
    }


5) Amenities, Facilities

views의 choices에 추가하자
rooms - views.py

    amenities = models.Amenity.objects.all()
    facilities = models.Facility.objects.all()

    choices = {
        "countries": countries,
        "room_types": room_types,
        "amenities": amenities,
        "facilities": facilities,
    }  

templates - rooms - search.html

        <div>
            <h3>Amenities</h3>
            <ul>
                {% for amenity in amenities %}
                    <li>
                        <label for="a_{{amenity.pk}}">{{amenity.name}}</label>
                        <input id="a_{{amenity.pk}}" name="amenities" type="checkbox">
                    </li>
                {% endfor %}
            </ul>
        </div>

        <div>
            <h3>Facilities</h3>
            <ul>
                {% for facility in facilities %}
                    <li>
                        <label for="f_{{facility.pk}}">{{facility.name}}</label>
                        <input id="f_{{facility.pk}}" name="facilities" type="checkbox">
                    </li>
                {% endfor %}
            </ul>
        </div>



자 그럼 이 많은 것들 중 어느게 해당하는지 (체크) 알 수 있을까? get으로 받아오자.

def search(request):
    ...
    s_amenities = request.GET.get("amenities")
    s_facilities = request.GET.get("facilities")
  
    form = {
    ...
        "s_amenities": s_amenities,
        "s_facilities": s_facilities,
    }

그리고 value를 넣어줘서 기억을 하게 해주자. 이때 value값으로 pk를 입력하지 않으면 해당 요소의 pk값이 아닌 그냥 'on'이 여러개 입력된다.
templates - rooms - search.html

        <div>
            <h3>Amenities</h3>
            <ul>
                {% for amenity in amenities %}
                    <li>
                        <label for="a_{{amenity.pk}}">{{amenity.name}}</label>
                        <input id="a_{{amenity.pk}}" name="amenities" type="checkbox" value="{{amenity.pk}}">
                    </li>
                {% endfor %}
            </ul>
        </div>

        <div>
            <h3>Facilities</h3>
            <ul>
                {% for facility in facilities %}
                    <li>
                        <label for="f_{{facility.pk}}">{{facility.name}}</label>
                        <input id="f_{{facility.pk}}" name="facilities" type="checkbox" value="{{facility.pk}}">
                    </li>
                {% endfor %}
            </ul>
        </div>


근데 s_amenities와 s_facilities가 출력이 잘 안된다.

    print(s_amenities, s_facilities)


QueryDict의 getlist 라는걸 써보자.

    s_amenities = request.GET.getlist("amenities")
    s_facilities = request.GET.getlist("facilities")



이제 저 체크박스들이 체크를 기억할 수 있게 해보자. 일단 유저가 체크한 내역들이 url에 입력이 되고 그걸 받아올 수 있으니 그걸 활용해서 조건문을 만들어보자.

    form = {
        "s_amenities": s_amenities,
        "s_facilities": s_facilities,
    }

search.html

        <div>
            <h3>Amenities</h3>
            <ul>
                {% for amenity in amenities %}
                    <li>
                        <label for="a_{{amenity.pk}}">{{amenity.name}}</label>
                        <input 
                            id="a_{{amenity.pk}}" 
                            name="amenities" 
                            type="checkbox" 
                            value="{{amenity.pk}}"
                            {% if amenity.pk in s_amenities %}
                                checked
                            {% endif %}
                        />
                    </li>
                {% endfor %}
            </ul>
        </div>

        <div>
            <h3>Facilities</h3>
            <ul>
                {% for facility in facilities %}
                    <li>
                        <label for="f_{{facility.pk}}">{{facility.name}}</label>
                        <input 
                            id="f_{{facility.pk}}" 
                            name="facilities" 
                            type="checkbox" 
                            value="{{facility.pk}}"
                            {% if facility.pk in s_facilities %}
                                checked
                            {% endif %}
                        />
                    </li>
                {% endfor %}
            </ul>
        </div>

하지만 체크가 되진 않는다.

왜냐면 조건문에서 amenity.pk는 integer인데 s_amenities는 string이기 때문이다.
여기서 우리는 slugify라는걸 써볼 것이다.
https://docs.djangoproject.com/en/4.0/ref/templates/builtins/#slugify
요롷게 쓰면 된다.

적용해보자

        <div>
            <h3>Amenities</h3>
            <ul>
                {% for amenity in amenities %}
                    <li>
                        <label for="a_{{amenity.pk}}">{{amenity.name}}</label>
                        <input 
                            id="a_{{amenity.pk}}" 
                            name="amenities" 
                            type="checkbox" 
                            value="{{amenity.pk}}"
                            {% if amenity.pk|slugify in s_amenities %}
                                checked
                            {% endif %}
                        />
                    </li>
                {% endfor %}
            </ul>
        </div>

        <div>
            <h3>Facilities</h3>
            <ul>
                {% for facility in facilities %}
                    <li>
                        <label for="f_{{facility.pk}}">{{facility.name}}</label>
                        <input 
                            id="f_{{facility.pk}}" 
                            name="facilities" 
                            type="checkbox" 
                            value="{{facility.pk}}"
                            {% if facility.pk|slugify in s_facilities %}
                                checked
                            {% endif %}
                        />
                    </li>
                {% endfor %}
            </ul>
        </div>


6) Instant Book & superhost

즉시 예약이 되는지 여부와 집주인이 superhost인지도 확인해보자.

search.html

        <div>
            <label for="instant">Instant Book Only?</label>
            <input value="{{instant}}", type="checkbox", id="instant", name="instant">
        </div>

        <div>
            <label for="superhost">By Superhost?</label>
            <input value="{{superhost}}", type="checkbox", id="superhost", name="superhost">
        </div>


이거를 백엔드에서 받아올 수 있게 하자.
views.py

def search(request):
  ...
    instant = request.GET.get("instant", False)
    super_host = request.GET.get("super_host", False)
    
    form = {
    ...
        "instant": instant,
        "super_host": super_host,  
    }

그리고 조건문을 넣어줘서 checked가 되도록 하자.
search.html

        <div>
            <label for="instant">Instant Book Only?</label>
            <input type="checkbox", id="instant", name="instant" {% if instant %}checked{% endif%}>
        </div>

        <div>
            <label for="super_host">By Superhost?</label>
            <input type="checkbox", id="super_host", name="super_host" {% if super_host %}checked{% endif%}>
        </div>

됐당

profile
맨땅에 헤딩. 인생은 실전.

0개의 댓글