🌈 Search Bar
🔥 수동으로 Search Bar 구현하기
🔥 Django From으로 Search Bar 구현하기
1. Search Bar 수작업으로 구현하기
- search bar는 목록, 상세보기 등 대부분 페이지에서 기능할 수 있도록 base.html에 만들어 볼께요.
- method는 get방식으로 받고, action은 form의 전송될 위치입니다. 전송될 위치에 대한 경로는 namespace와 name을 통해 설정하면 간편합니다.
- 🔎
<form method="get" action="{% url "rooms:search" %}"></form>
- input의 name속성은 argument의 key값입니다.
- 또한 form을 {% block search-bar % } ~ {% endblock search-bar %}으로 감싼 이유는 base.html은 모든 페이지에 적용되는데, 때론 search bar가 나타나지 않아야할 페이지도 필요하기 때문이에요. 필요하지 않은 페이지에서 {% block search-bar % } ~ {% endblock search-bar %}를 다시 선언하면 덮어씌어져서 해당 페이지에서는 나타나지 않는답니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block page_title %}{% endblock page_title %}| Nbnb</title>
</head>
<body>
<header>
{% include "partials/nav.html" %}
{% block search-bar %}
<form method="get" action="{% url "rooms:search" %}">
<input name="city" placeholder="Search by City">
</form>
{% endblock search-bar %}
</header>
{% block content %}{% endblock content %}
{% include "partials/footer.html" %}
</body>
</html>
2) URL 및 View 기본 세팅
- search bar에 입력 후 전송하면, views.py의 search함수가 작동하게 Url을 매핑하였어요.
from django.urls import path
from . import views
app_name = "rooms"
urlpatterns = [
path("<int:pk>", views.RoomDetail.as_view(), name="detail"),
path("search/", views.search, name="search"),
]
- 템플릿에서 action의 경로 설정을 해줬기 때문에 form 양식에 입력한 결과를 전송하면 search 함수가 실행됩니다.
- 사용자가 입력한 argument값을 get을 통해 url에서 views.py로 가져올 수 있어요.
- 🔎 city = request.GET.get("city")
- "seoul"을 입력하면, "http://127.0.0.1:8000/rooms/search/?city=seoul"로 이동하고, console에는 "Seoul"이 출력되는 것을 볼 수 있네요.
from django.shortcuts import render
from . import models
...
...
def search(request):
city = request.GET.get("city")
city = str.capitalize(city)
context = {"city": city}
return render(request, "rooms/search.html", context)
- form을 통한 입력값이 html로 render 되는지 확인해보기 위해 "rooms/search.html"를 간단히 작성해 볼께요. 사용자의 입력을 url에서 받아온 뒤, 다시 템플릿 변수로 전달하였어요.
- {% block search-bar %} ~ {% endblock search-bar % }를 search.html에서 다시 지정한 이유는 search의 결과물인 페이지이기 때문에 search bar를 가리기 위해서에요. 덮어씌우면 기존 base.html의 serach bar는 나타나지 않아요.
{% extends "base.html" %}
{% block page_title %}
Search
{% endblock page_title %}
{% block search-bar %} 👈 base.html에 search-bar를 덮어씌어 안보이게 처리할 수 있답니다:)
{% endblock search-bar %}
{% block content %}
<h2>Search!</h2>
<h4>Search By {{city}}</h4>
{% endblock content %}
3) 다중 검색
- 도시, 국가, room_type 등을 동시에 검색해서 이에 해당하는 정보만 조회할 수 있게 하는 search bar를 만들려고 해요.
- form을 통해 사용자가 입력 또는 선택한 정보들이 url의 query로 나타날 수 있게 해야겠죠.
- Model에서 사용한 패키지인 Django_country를 가져올께요. Django_Counry는 바로 Context로 넘겨줄 수 있어요. console의 Object가 나타나는 것을 볼 수 있어요. import는 필요하니다.
- 🔎 from django_countries import countries
- RoomType 모델에서 모든 객체를 가져와서 context로 넘겨주면, 템플릿에서 RoomType를 선택 목록으로 사용할 수 있답니다.
from django.shortcuts import render
from django_countries import countries
from . import models
...
...
def search(request):
city = request.GET.get("city", "Anywhere")
city = str.capitalize(city)
room_types = models.RoomType.objects.all()
context = {
"city": city,
"countries": countries,
"room_types": room_types,
}
return render(request, "rooms/search.html", context)
- city는 사용자에게 직접 입력받지만, country와 room_type는 선택목록으로 만들었어요.
- option 태그의 vaule값을 지정을 통해 선택된 값을 url에 argument로 나타낼 수 있어요. 또한 .code를 통해 DB의 값을 이용할 수 있답니다.
- 🔎
<option value="{{country.code}}">{{country.name}}</option>
- 🔎
<option value="{{room_type.pk}}">{{room_type.name}}</option>
# rooms/search.html
{% extends "base.html" %}
{% block page_title %}
Search
{% endblock page_title %}
{% block search-bar %} 👈 base.html의 search bar는 우선 숨기고 밑에서 test해볼께요:)
{% endblock search-bar %}
{% 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" type="text">
</div>
<div>
<label for="country">Country</label>
<select id="country" name="country">
{% for country in countries %} 👈 django_counries를 목록으로 나타내줘요.
<option value="{{country.code}}">{{country.name}}</option>
{% endfor %}
</select>
</div>
<div>
<label for="room_type">Room Types</label>
<select id="room_type" name="room_type">
{% for room_type in room_types %} 👈 room_types를 선택목록을 나타내줘요.
<option value="{{room_type.pk}}">{{room_type.name}}</option>
{% endfor %}
</select>
</div>
<button>Search</button>
</form>
{% endblock content %}
4) selected 속성
- "seoul", "South Korea", "Hotel Room"을 선택하고 search 버튼을 누르면, 현재 form에 입력한 값이 url에 잘 표현되고 있어요. 참고로 form안에 button이 1개라면, submit과 같은 기능을합니다.
- 문제는 전송버튼을 누르면, 선택된 값이 url에는 잘 나타나지만 form이 초기화되는 현상이 발생합니다. 현재 form에서는 선택된 값을 기억하지 못하기 때문이에요. 이를 보완해볼께요.
- 우선 선택된 값이 url에는 잘 표시되기 때문에 views.py에서 해당 argument를 가져올께요. 값이 없는데 요구하면 오류가 발생하기 때문에 Default 값으로 "KR"과 0을 각 각 지정했어요. 또한 url의 값을 get으로 가져오면 str 형태에요,, room_types의 경우에는 int로 수정해주었어요!
- 🔎 country = request.GET.get("country", "KR")
- 🔎 room_type = int(request.GET.get("room_type", 0))
from django.shortcuts import render
from django_countries import countries
from . import models
...
def search(request):
city = request.GET.get("city", "Anywhere")
city = str.capitalize(city)
country = request.GET.get("country", "KR")
room_type = request.GET.get("room_type", 0)
room_types = models.RoomType.objects.all()
context = {
"city": city,
"countries": countries,
"country": country,
"room_types": room_types,
"current_room_type": room_type,
}
return render(request, "rooms/search.html", context)
- 이름이 비슷해서 헷갈릴 수 있어요. 이를 좀 더 명확히 구분할 수 있도록 나눠볼께요.
- context를 form과 choices로 분리시켯어요. form은 사용자가 입력 또는 선택한 값을 url로 받아온 것이고, choices는 선택 목록을 제공해주기 위한 변수입니다.
- 이렇게 둘 이상의 변수를 병합하여 템플리 변수로 render하기 위해서 아래와 같이 unpack(
**
)을 사용하여 처리해줄 수 있어요.
return render(request, "rooms/search.html", {**form, **choices})
from django.shortcuts import render
from django_countries import countries
from . import models
...
def search(request):
city = request.GET.get("city", "Anywhere")
city = str.capitalize(city)
country = request.GET.get("country", "KR")
room_type = int(request.GET.get("room_type", 0)) 👈 url에서 넘어온 값을 int로 변환
room_types = models.RoomType.objects.all()
form = {
"city": city,
"s_country": country,
"s_room_type": room_type,
}
choices = {
"countries": countries,
"room_types": room_types,
}
return render(request, "rooms/search.html", {**form, **choices})
- {% if %} ~ {% endif %}를 통해 url의 값과 선택된 값이 일치하는 경우에 option 태그의 selected 속성을 활성화시켰어요.
- "Any Kind"는 어떤 roon_type이라도 상관없을 경우를 대비해 0번을 value로하여 새로 생성해주었어요. 이처럼 선택할 수 있는 값을 더 제공하고 싶다면 수동으로도 생성해줄수도 있답니다.
{% extends "base.html" %}
{% block page_title %}
Search
{% endblock page_title %}
{% block search-bar %}
{% endblock search-bar %}
{% 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" type="text">
</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 Types</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 %}
5) checked
- 선택목록은 selected를 통해 사용자가 선택한 값을 기억하게 했는데요,, checkbox는 checked를 통해 값을 기억하게 할 수 있어요.
- 단, check 박스는 선택 결과가 1개일 수도 있고 더 많을 수도 있기 때문에 사용자가 선택한 값들을 getlist로 받아올 수 있습니다. getlist는 그 값들을 배열 형태로 가져옵니다.
- 🔎 request.GET.getlist("name값")
- intant와 superhost는 체크박스이지만 True, False값만 가지면 되요, 여러개의 체크가 존재하는게 아니니까요! 체크가 되지 않는 False일 때는 문제없지만 True일 때, 값을 "on"을 가지기 때문에 bool()로 형변환을 해줘야합니다:)
from django.shortcuts import render
from django_countries import countries
from . import models
...
...
def search(request):
city = request.GET.get("city", "Anywhere")
city = str.capitalize(city)
country = request.GET.get("country", "KR")
room_type = int(request.GET.get("room_type", 0))
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))
instant = bool(request.GET.get("instant", False))
superhost = bool(request.GET.get("superhost", False))
s_amenities = request.GET.getlist("amenities")
s_facilities = request.GET.getlist("facilities")
form = {
"city": city,
"s_country": country,
"s_room_type": room_type,
"price": price,
"guests": guests,
"bedrooms": bedrooms,
"beds": beds,
"baths": baths,
"instant": instant,
"superhost": superhost,
"s_amenities": s_amenities,
"s_facilities": s_facilities,
}
room_types = models.RoomType.objects.all()
amenities = models.Amenity.objects.all()
facilities = models.Facility.objects.all()
choices = {
"countries": countries,
"room_types": room_types,
"amenities": amenities,
"facilities": facilities,
}
return render(request, "rooms/search.html", {**form, **choices})
- 이제 getlist를 통해 사용자가 체크한 값들을 view에서 템플릿으로 전달할 수 있어요.
- 이에 pk값이 list 내에 포함되있는지 확인해서,, list에 포함되어있다면 checked를 활성화시켜주면 되요:)
- 다만, pk값은 int형이고, getlists 넘어온 list는 str이에요. int형을 str로 바꿀수 있는 템플릿 필터인 slugify를 사용하여 캐스팅을 해줄께요!
{% extends "base.html" %}
{% block page_title %}
Search
{% endblock page_title %}
{% block search-bar %}
{% endblock search-bar %}
{% block content %}
...
...
<div>
<label for="instatn">Instant Book Only?</label>
<input type="checkbox" name="instant" id="instant" {% if instant %}checked{% endif %}>
</div>
<div>
<label for="super_host">By Superhost Only?</label>
<input type="checkbox" name="superhost" id="superhost" {% if superhost %}checked{% endif %}>
</div>
<div>
<div>
<h3>Amenities</h3>
<ul>
{% for amenity in amenities %}
<li>
<label for="a_{{amenity.pk}}">{{amenity.name}}</label>
<input
type="checkbox"
value="{{amenity.pk}}"
name="amenities"
id="a_{{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
type="checkbox"
value="{{facility.pk}}"
name="facilities"
id="f_{{facility.pk}}"
{% if facility.pk|slugify in s_facilities %}
checked
{% endif %}
/>
</li>
{% endfor %}
</ul>
</div>
<button>Search</button>
</form>
{% endblock content %}
6) conditional filtering
- 이제 검색에 필요한 값을 입력 또는 선택할 수도 있고, 이를 기억할 수도 있어요,, 그렇다면, 선택된 값들에 해당되는 정보를 출력해주는 기능이 필요하겠죠. DB에서 어떤 기준에 해당하는 자료를 추출해오는 것은 filter()를 통해 할 수 있어요.
- filter()에 전달한 파라미터는 Dict 형태로 만들어 한번에 전달하면 편리합니다.
filter_args = {}
rooms = models.Room.objects.filter(**filter_args)
- Field lookups를 참고하면 filter를 더 효과적으로 다룰 수 있는 검색 기능을 사용할 수 있어요!
__starswith
, __lte
, __gte
, __pk
, contain
등..
- s_facilities, s_facilities는 getlist로 값을 받아오기 때문에 list형태 입니다. 또한 그 안에 값은 str이죠. 이에 for문으로 1개씩 꺼내서 pk값을 key값으로 filter_args에 추가해줄께요.
from django.shortcuts import render
from django_countries import countries
from . import models
...
...
def search(request):
city = request.GET.get("city", "Anywhere")
city = str.capitalize(city)
country = request.GET.get("country", "KR")
room_type = int(request.GET.get("room_type", 0))
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))
instant = bool(request.GET.get("instant", False))
superhost = bool(request.GET.get("superhost", False))
s_amenities = request.GET.getlist("amenities")
s_facilities = request.GET.getlist("facilities")
form = {
"city": city,
"s_country": country,
"s_room_type": room_type,
"price": price,
"guests": guests,
"bedrooms": bedrooms,
"beds": beds,
"baths": baths,
"instant": instant,
"superhost": superhost,
"s_amenities": s_amenities,
"s_facilities": s_facilities,
}
room_types = models.RoomType.objects.all()
amenities = models.Amenity.objects.all()
facilities = models.Facility.objects.all()
choices = {
"countries": countries,
"room_types": room_types,
"amenities": amenities,
"facilities": facilities,
}
filter_args = {}
if city != "Anywhere":
filter_args["city__startswith"] = city
filter_args["country"] = country
if room_type != 0:
filter_args["room_type__pk"] = room_type
if price != 0:
filter_args["price__lte"] = price
if guests != 0:
filter_args["guests__gte"] = guests
if bedrooms != 0:
filter_args["bedrooms__gte"] = bedrooms
if beds != 0:
filter_args["beds__gte"] = beds
if baths != 0:
filter_args["baths__gte"] = baths
if instant is True:
filter_args["instant_book"] = True
if superhost is True:
filter_args["host__superhost"] = True
if len(s_amenities) > 0:
for s_amenity in s_amenities:
filter_args["amenities__pk"] = int(s_amenity)
if len(s_facilities) > 0:
for s_facility in s_facilities:
filter_args["facilities__pk"] = int(s_facility)
rooms = models.Room.objects.filter(**filter_args)
return render(request, "rooms/search.html", {**form, **choices, "rooms": rooms})
- 템플릿에 rooms로 전달했기 때문에 for문을 사용해서 해당되는 방을 출력할 수 있어요. form문 아래에 간단히 작성해볼께요.
...
...
<h3>Results</h3>
{% for room in rooms %}
<h3>{{room.name}}</h3>
{% endfor %}
2. Django From으로 Search Bar 구현하기
- Model을 만드는것처럼 forms.py에 Class를 만들어 Django Form을 상속받아 사용하면, 더 간편하게 form을 만들 수 있어요.
- Django가 제공하는 forms를 가져온 뒤, Form을 상속받아 사용합니다.
from django import forms
class SearchForm(forms.Form):
"""Search Form Definition"""
city = forms.CharField()
price = forms.IntegerField()
- forms.py에서 만든 Django Form인 SearchForm을 변수에 담아 템플릿에 render해볼께요:)
from django.shortcuts import render
from django_countries import countries
from . import models, forms
...
...
def search(request):
form = forms.SearchForm()
context = {"form": form}
return render(request, "rooms/search.html", context)
- form 태그안에 View에서 전달받은 {{form}}을 넣어준 것뿐이에요. method와 action을 달라진게 없어요!
- city에는 seoul, price에는 100000을 입력하고 url을 살펴볼께요. name을 지정하지 않았는데도 "?city=seoul&price=100000"으로 나타나네요,, Django에서 알아서 설정해주는군요!
{% extends "base.html" %}
{% block page_title %}
Search
{% endblock page_title %}
{% block search-bar %}
{% endblock search-bar %}
{% block content %}
<h2>Search!</h2>
<form method="get" action="{% url "rooms:search" %}">
{{form}}
<button>Search</button>
</form>
{% endblock content %}
- 입력창이 옆으로 나란히 나타나는걸 볼 수 있어요. 페이지 소스보기를 통해 확인해보면
<tr>
, <th>
, <td>
로 자동 설정되었네요. .as_p
, .as_ul
, .as_table
로 형식을 바꿀 수 있답니다.
- p태그처럼 사용하기 : 🔎 {{form.as_p}}
- 참고 : https://docs.djangoproject.com/en/2.2/ref/forms/fields/
- 텍스를 입력 받을 때는 CharFied, 숫자는 IntegerField, 단일선택목록은 ModelChoiceField, True&False 체크박스는 BooleanField로 form을 만들어 줄 수 있어요!
- amenities와 facilities와 같은 다중 선택 목록은 ModelMultipleChoiceField를 사용해요. widget을 통해 더 편리하게 이용할 수 있도록 form의 형태를 바꿔줍니다. checkbox형태로 다중 선택할 수 있도록 CheckboxSelectMultiple를 적용했어요.
- Django_Counry는 외부 라이브러리이기 때문에 Django_Country 문서를 참고하였습니다.
- 🔎 from django_countries.fields import CountryField
- 🔎 counrty = CountryField(default="KR").formfield() 👈 기본값으로 KR을 지정
- 또한 field안에 여러 속성을 통해 Django Form을 제어할 수 있답니다:)
- 🔎 initial="" : CharField에서 빈 form 대신 초기값을 넣어둘 수 있어요.
- 🔎 empty_label="" : 선택목록을 제공하는 ModelChoiceField의 선택안함 필드의 이름을 정해줘요. empty_label이 없으면 "----"의 선택 값이 기본으로 주어집니다.
- 🔎 queryset="" : 모델에서 데이터를 가져와 선택목록을 제공합니다.
- 🔎 required="" : field 값을 비어두어도 전송이 가능합니다.
from django import forms
from django_countries.fields import CountryField
from . import models
class SearchForm(forms.Form):
"""Search Form Definition"""
city = forms.CharField(initial="Anywhere")
country = CountryField(default="KR").formfield()
room_type = forms.ModelChoiceField(
empty_label="Any Kind", queryset=models.RoomType.objects.all(), required=False
)
price = forms.IntegerField(required=False)
guests = forms.IntegerField(required=False)
bedrooms = forms.IntegerField(required=False)
beds = forms.IntegerField(required=False)
baths = forms.IntegerField(required=False)
instant_book = forms.BooleanField(required=False)
superhost = forms.BooleanField(required=False)
amenities = forms.ModelMultipleChoiceField(
queryset=models.Amenity.objects.all(),
widget=forms.CheckboxSelectMultiple,
required=False,
)
facilities = forms.ModelMultipleChoiceField(
queryset=models.Facility.objects.all(),
widget=forms.CheckboxSelectMultiple,
required=False,
)
- Django Form을 사용하면, name, id, for를 지정하지 않아도, Django에서 알아서 입력해줍니다. 템플릿 파일에 html 태그를 하나하나 작성하는 수고로움을 덜어줍니다. 또한 입력 및 선택한 값들이 모두 url에 argument로 나타나는 것을 볼 수 있어요. html 태그와 그 속성이 어떻게 세팅되었는지 궁금하다면 페이지 소스보기를 참고하세요.
4) request.GET
- 이제 입력 및 체크한 값들을 기억할 수 있게 해줄께요. Django form을 사용하지 않았을 때는 url에 argument를 GET.get 또는 GET.getlist 통해 각 argument를 받아온 뒤, 다시 템플릿으로 render했었어요. Django Form을 사용한다면 request.GET만 Form에 전달해주면 됩니다!
- 전송버튼을 눌러도 계속 값을 가지고 있어요!
def search(request):
form = forms.SearchForm(request.GET)
context = {"form": form}
return render(request, "rooms/search.html", context)
5) is_valid()
- 처음 접근할 때는 기억하는 값이 없을수 밖에 없겟죠. 이럴 때 required 오류메시지가 해당 필드아래 나타냅니다. 값이 있을 때는 기억하고, 없을 때는 빈 form을 보여주겠끔 처리할 수 있어요.
from django.shortcuts import render
from django_countries import countries
from . import models, forms
...
...
def search(request):
country = request.GET.get("country")
if country:
form = forms.SearchForm(request.GET)
if form.is_valid():
print(form.cleaned_data)
else:
form = forms.SearchForm()
context = {"form": form}
return render(request, "rooms/search.html", context)
- is_value()로 처리해주면, form에 입력된 값의 유효성 검사를 해줘요. 예를들어 IntegerField에 text를 넣었다면 문제가 있음을 알려주죠. 유효성 검사를 마친 데이터들을 cleaned_data로 출력해보면 아래처럼 나타나는 것을 볼 수 있습니다.
- 입력한 데이터 뿐 아니라, 선택된 데이터를 DB에서 찾아와 갖고 있어요! 또한 url에서 on으로 나타나는 값을 True로 자동으로 변환해둡니다. bool, int 등의 형변환을 대신 해주죠!
{'city': 'seoul', 'country': 'KR', 'room_type': <RoomType: Private Room>,
'price': None, 'guests': None, 'bedrooms': None, 'beds': None, 'baths':
None, 'instant_book': False, 'superhost': True, 'amenities': <QuerySet
[<Amenity: Shower>, <Amenity: Wifi>, <Amenity: Washer>]>, 'facilities':
<QuerySet [<Facility: Park>, <Facility: Elevator>, <Facility: Gym>]>}
6) conditional filtering
- cleaned_data로 argument의 값들이 정리되어 올 뿐 아니라, DB의 QuerySet형태로 있기 때문에 search argument를 만드는 것도 비교적 수월해요! 형변환이나,
__pk
등이 필요 없거든요!
from django.shortcuts import render
from django_countries import countries
from . import models, forms
...
...
def search(request):
country = request.GET.get("country")
if country:
form = forms.SearchForm(request.GET)
if form.is_valid():
city = form.cleaned_data.get("city")
country = form.cleaned_data.get("country")
room_type = form.cleaned_data.get("room_type")
price = form.cleaned_data.get("price")
guests = form.cleaned_data.get("guests")
bedrooms = form.cleaned_data.get("bedrooms")
beds = form.cleaned_data.get("beds")
baths = form.cleaned_data.get("baths")
instant_book = form.cleaned_data.get("instant_book")
superhost = form.cleaned_data.get("superhost")
amenities = form.cleaned_data.get("amenities")
facilities = form.cleaned_data.get("facilities")
filter_args = {}
if city != "Anywhere":
filter_args["city__startswith"] = city
filter_args["country"] = country
if room_type is not None:
filter_args["room_type"] = room_type
if price is not None:
filter_args["price__lte"] = price
if guests is not None:
filter_args["guests__gte"] = guests
if bedrooms is not None:
filter_args["bedrooms__gte"] = bedrooms
if beds is not None:
filter_args["beds__gte"] = beds
if baths is not None:
filter_args["baths__gte"] = baths
if instant_book is True:
filter_args["instant_book"] = True
if superhost is True:
filter_args["host__superhost"] = True
for amenity in amenities:
filter_args["amenities"] = amenity
for facility in facilities:
filter_args["facilities"] = facility
rooms = models.Room.objects.filter(**filter_args)
else:
form = forms.SearchForm()
context = {"form": form, "rooms": rooms}
return render(request, "rooms/search.html", context)
- search 함수는 FBV죠,, CBV로 사용할 수 있답니다. View를 import한 후, class안에 search함수를 넣어주기만 하면 되요. 대신 request뿐아니라 self도 받아야겠죠. 더불어 rooms/urls.py에서 매핑할 view를 CBV이름으로 연결하고 as_view()를 설정해주세요:)
rooms/urls.py
from django.urls import path
from . import views
app_name = "rooms"
urlpatterns = [
path("<int:pk>", views.RoomDetail.as_view(), name="detail"),
path("search/", views.SearchView.as_view(), name="search"),
]
- paginator를 적용시켰는데, consoel에 "UnorderedObjectListWarning"이 발견된다면, QuerySet에 정렬이 필요하단 경고에요. order_by로 정렬을 해주면 됩니다.
- 🔎
qs = models.Room.objects.filter(**filter_args).order_by("-created")
from django.views.generic import ListView, DetailView, View
from django.shortcuts import render
from django.core.paginator import Paginator
from . import models, forms
...
...
class SearchView(View):
def get(self, request):
country = request.GET.get("country")
if country:
form = forms.SearchForm(request.GET)
if form.is_valid():
...
...
...
qs = models.Room.objects.filter(**filter_args).order_by("-created")
paginator = Paginator(qs, 10, orphans=5)
page = request.GET.get("page", 1)
rooms = paginator.get_page(page)
context = {"form": form, "rooms": rooms}
return render(request, "rooms/search.html", context)
else:
form = forms.SearchForm()
context = {"form": form}
return render(request, "rooms/search.html", context)