에어비앤비에 있는 숙소 검색창
어떤 방들이 있는지 검색해보려 한다.
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에 있던
을 긁어다가 다른데로 옮기자. 그리고 여기 있는 는 로 바꾼다.<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})
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>
그럼 이제 잘 뜬다.
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 %}
이제 잘 고정되어있다.
이제 다른 항목들도 검색창으로 추가해보자.
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,
}
짠
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>
짠
즉시 예약이 되는지 여부와 집주인이 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>
됐당