rooms/views.py
from django.shortcuts import render
from django_countries import countries
...
def search(request):
city = request.GET.get("city", "Anywhere")
city = str.capitalize(city)
country = request.GET.get("country", "KR") #Default 값으로 KR
room_type = int(request.GET.get("room_type", 0)) #Default 값으로 0, url 값을 get으로 가져오면 str형태이기 때문에 int로 수정
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))
super_host = bool(request.GET.get("superhost", False))
s_amenities = request.GET.getlist("amenities")
s_facilities = request.GET.getlist("facilities")
#단, check 박스는 선택 결과가 1개일 수도 있고 더 많을 수도 있기 때문에 사용자가 선택한 값들을 getlist로 받아올 수 있다. getlist는 그 값들을 배열 형태로 가져온다.
form = {
"city": city,
"s_room_type": room_type,
"s_country": country,
"price": price,
"guests": guests,
"bedrooms": bedrooms,
"beds": beds,
"baths": baths,
"s_amenities": s_amenities,
"s_facilities": s_facilities,
"instant": instant,
"superhost": superhost,
}
room_types = models.RoomType.objects.all()
amenities = models.Amenity.objects.all()
facilities = models.Facility.objects.al
choices = {
"countries": countries,
"room_types": room_types,
"amenities": amenities,
"facilities": facilities,
}
filter_args = {} #filter()에 들어갈 parameter를 filter_args에 담아서 unpack
if city != "Anywhere":
filter_args["city__startswith"] = city
filter_args["country"] = country
if room_type != 0:
filter_args["room_type__pk"] = room_type #Foreign Key 이용
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) #filter를 이용해서 dictionary로 만들어 한번에 전달하면 편리하다.
return render(request, "rooms/search.html", {**form, **choices, "rooms": rooms})
rooms/urls.py
urlpatterns = [
path("<int:pk>", views.RoomDetail.as_view(), name="detail"),
path("search/", views.search, name="search"), #다음과 같이 추가, search 다음에 꼭 /를 붙여준다. 안 그럼 에러남
]
templates/rooms/search.html
{% 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" />
</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>
<div>
<label for="price">Price</label>
<input value="{{price}}" type="number" name="price" id="price" placeholder="Price" />
</div>
<div>
<label for="guests">Guests</label>
<input value="{{guests}}" type="number" name="guests" id="guests" placeholder="Guests" />
</div>
<div>
<label for="bedrooms">Bedrooms</label>
<input value="{{bedrooms}}" type="number" name="bedrooms" id="bedrooms" placeholder="Bedrooms" />
</div>
<div>
<label for="beds">Beds</label>
<input value="{{beds}}" type="number" name="beds" id="beds" placeholder="Beds" />
</div>
<div>
<label for="baths">Baths</label>
<input value="{{baths}}" type="number" name="baths" id="baths" placeholder="Baths" />
</div>
<div>
<label for="instant">Instant Book Only?</label>
<input type="checkbox" name="instant" id="instant" {% if instant %}checked{% endif %} />
</div>
<div>
<label for="superhost">By Superhost Only?</label>
<input type="checkbox" name="superhost" id="superhost" {% if superhost %}checked{% endif %} />
</div>
<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>
<button>Search</button>
</form>
<h3>Results</h3>
#template에 rooms로 전달했기 때문에 for문 이용하여 방 출력
{% for room in rooms %}
<h3>{{room.name}}</h3>
{% endfor %}
{% endblock content %}
templates/partials/nav.html
<a href="{% url "core:home" %}">Nbnb</a>
<ul>
<li><a href="#">Login</a></li>
</ul>
templates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<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 %}
{% include "partials/footer.html" %}
</body>
</html>
Django Form으로 Search Bar 구현
forms.py에 class를 만들어서 Django Form을 받아 상속받아 사용하면 간편하게 form을 만들 수 있다.
Django가 제공하는 forms를 가져온뒤 Form을 상속받아 이용하면 된다. forms.Form 👉 이렇게
amenities,facilities의 경우에는 ModelMultipleChoiceField를 사용해서 widget을 통해 form 형태를 바꿔준다.
또한 checkbox 형태로 다중 선택할 수 있도록 ChecboxSelectMultiple 적용
rooms/forms.py
from django import forms
from django_countries.fields import CountryField
from . import models
class SearchForm(forms.Form):
city = forms.CharField(initial="Anywhere")
country = CountryField(default="KR").formfield()
room_type = forms.ModelChoiceField(required=False, empty_label="Any kind", queryset=models.RoomType.objects.all())
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(
required=False,
queryset=models.Amenity.objects.all(),
widget=forms.CheckboxSelectMultiple,
)
facilities = forms.ModelMultipleChoiceField(
required=False,
queryset=models.Facility.objects.all(),
widget=forms.CheckboxSelectMultiple,
)
rooms/views.py
from django.views.generic import ListView, DetailView, View #View import
from django.shortcuts import render
from django.core.paginator import Paginator
from . import models, forms
class HomeView(ListView):
"""HomeView Definition"""
model = models.Room
paginate_by = 10
paginate_orphans = 5
ordering = "created"
context_object_name = "rooms"
class RoomDetail(DetailView):
"""RoomDetail Definition"""
model = models.Room
class SearchView(View):
"""SearchView Definition"""
def get(self, 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
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)
return render(
request, "rooms/search.html", {"form": form, "rooms": rooms}
)
else:
form = forms.SearchForm()
return render(request, "rooms/search.html", {"form": form})
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"),
]
templates/rooms/search.html
{% 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.as_p}} #p 태그처럼 사용하기
<button>Search</button>
</form>
<h3>Results</h3>
{% for room in rooms %}
<h3>{{room.name}}</h3>
{% endfor %}
{% endblock content %}