Django의 Forms API라는걸 사용해볼 것이다.
https://docs.djangoproject.com/en/4.0/ref/forms/api/
우선 기존의 내용을 다 지운다. 그리고 새로운 forms.py를 만든다.
rooms - views.py
...
def search(request):
form = forms.SearchForm()
return render(
request,
"rooms/search.html",
context={"form": form},
)
rooms - forms.py
from django import forms
class SearchForm(forms.Form):
pass
templates - rooms - search.html
...
{% block content %}
<h2>Search!</h2>
<form method="get" action="{% url 'rooms:search' %}">
{{form}}
<button>Search</button>
</form>
<h3>Results</h3>
{% for room in rooms %}
<h4>{{ room.name }}</h4>
{% endfor %}
{% endblock content %}
아직 검색 페이지는 아무것도 안뜬다.
rooms - forms.py
from django import forms
class SearchForm(forms.Form):
city = forms.CharField(initial="Anywhere")
price = forms.IntegerField()
강의에서는 코드를 확인해보면 테이블이 생성되었는데 장고 버전이 바뀐 탓인지 여기는 label과 input만 있다.
여튼 페이지에 나타나는 모양을 조금 바꿔보자.
렌더링시에 <p> 형태로 뜨게 해준다.
templates - rooms - search.html
...
<form method="get" action="{% url 'rooms:search' %}">
{{form.as_p}}
<button>Search</button>
</form>
...
room_type도 추가하자.
rooms - forms.py
class SearchForm(forms.Form):
city = forms.CharField(initial="Anywhere")
price = forms.IntegerField(required=False)
room_type = forms.ModelChoiceField(queryset=models.RoomType.objects.all())
국가명도 검색창에 넣을건데 django-countries를 살펴보자. formfield라는걸 사용할 것이다.
rooms - forms.py
class SearchForm(forms.Form):
city = forms.CharField(initial="Anywhere")
country = CountryField().formfield()
price = forms.IntegerField(required=False)
room_type = forms.ModelChoiceField(queryset=models.RoomType.objects.all())
지구 어디든지 있는 남극의 4달러짜리 호텔은 역시나 검색이 되지 않는다.
조금 더 수정. 기본값을 지정해줄건데 국가는 한국, 숙소 형태는 '----' 형태가 아니라 Any kind로 지정해놓을 것이다.
rooms - forms.py
class SearchForm(forms.Form):
city = forms.CharField(initial="Anywhere")
country = CountryField(default="KR").formfield()
price = forms.IntegerField(required=False)
room_type = forms.ModelChoiceField(
empty_label="Any kind", queryset=models.RoomType.objects.all()
)
이제 침실, 침대수, 욕조수 등 숫자 필드에 대해서 추가해주자.
rooms - forms.py
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)
help_text 라는걸 써보자.
models.py에서 guests에 help_text를 적용해보았다.
rooms - forms.py
guests = models.IntegerField(help_text="How many people will be staying?")
그러면 admin 페이지에서 이렇게 뜬다.
forms에도 이렇게 할수 있다.
Form Field는 Widget을 render한다. Widget은 HTML요소인데 Form Field에서 바꿀 수 있다.
https://docs.djangoproject.com/en/4.0/ref/forms/fields/#widget
ChaField - Field를 들어가보면 widget이 TextInput 클라스로 되어있다.
TextInput 클래스는 다음과 같이 정의되어있다. 아마 입력하는 데이터 종류가 "text"로 한정되어있고 특정한 페이지를 렌더링하도록 지정된 것 같다.
TextInput이 상속받는 Input은 다음과 같다.
무튼 그래서 시험삼아 CharField에 해당하는 요소 하나의 widget을 바꿔보자.
rooms - forms.py
...
class SearchForm(forms.Form):
city = forms.CharField(initial="Anywhere", widget=forms.Textarea)
...
전)
후)
이제 amenities와 facilities를 추가하자.
rooms - forms.py
class SearchForm(forms.Form):
...
amenities = forms.ModelMultipleChoiceField(queryset=models.Amenity.objects.all())
facilities = forms.ModelMultipleChoiceField(queryset=models.Facility.objects.all())
Amenities와 Facilities가 추가되었다. Ctrl 키를 사용해서 여러개를 동시에 선택할 수도 있다.
괜찮긴한데 모양이 그렇게 좋진 않다. widget을 다른 것으로 바꿔보자.
rooms - forms.py
...
class SearchForm(forms.Form):
...
amenities = forms.ModelMultipleChoiceField(
queryset=models.Amenity.objects.all(), widget=forms.CheckboxSelectMultiple
)
facilities = forms.ModelMultipleChoiceField(
queryset=models.Facility.objects.all(), widget=forms.CheckboxSelectMultiple
)
짠
주소창에도 클릭한게 반영이 된다. 다만 체크박스는 초기화된다. 강의에서 다룬 장고는 예전버전으로 URL에도 남아있지가 않는 모양이다.
rooms - views.py
def search(request):
form = forms.SearchForm(request.GET)
return render(
request,
"rooms/search.html",
context={"form": form},
)
form은 HTML을 빠르게 만들어준다. 그리고 데이터도 정리해준다. 즉 이상한 무언가가 없게끔 확인해준다.
가령 url에서는 숫자로 입력되지만 실제 글자 입력시에는 string인 것들을 자동으로 변환해준다.
rooms - views.py
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()
...
보면 IntegerField는 따옴표없이 숫자로만 적혀있고 BooleanField의 경우 True/False로만 표기되는 것을 볼 수 있다. ManyToManyField의 경우 QuerySet을 주었다.
rooms - views.py
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 != 0:
filter_args["room_type"] = 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_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)
print(filter_args)
else:
form = forms.SearchForm()
return render(
request,
"rooms/search.html",
context={"form": form, "rooms": rooms},
)
원래 강의에선 이렇게 하고 검색하면 에러가 뜨는데 여기선 안뜬다. 콘솔에 뜨는 내용도 다르다.
일단 강의내용이 무슨 말인지 이해가 정확히 안되서 일단 코드 따라만 함.
rooms - views.py
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
rooms = models.Room.objects.filter(**filter_args)
else:
form = forms.SearchForm()
return render(
request,
"rooms/search.html",
context={"form": form, "rooms": rooms},
)
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"),
]
손을 조금 더 보자.
rooms - views.py
from django.views.generic import ListView, DetailView, View
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)
paginator = Paginator(qs, 10, orphans=5)
page = request.GET.get("page", 1)
rooms = paginator.get_page(page)
else:
form = forms.SearchForm()
return render(
request,
"rooms/search.html",
context={"form": form, "rooms": rooms},
)
rooms 가 먼저 참조되었다고 한다.
return 부분을 if 와 else 부분에 나눠서 넣어주자.
rooms - views.py
def get(self, request):
country = request.GET.get("country")
if country:
form = forms.SearchForm(request.GET)
if form.is_valid():
...
return render(
request,
"rooms/search.html",
context={"form": form, "rooms": rooms},
)
else:
form = forms.SearchForm()
return render(
request,
"rooms/search.html",
context={"form": form},
)
만약 country가 없으면 (else) 빈 form을 만들어낸다.
잘 나온다.
이렇게 하면 url에다가 장난치는것도 방지할 수 있다고 한다.
근데 이런 저런 조건을 넣어서 검색해보면 페이지는 뜨는데 콘솔에 에러가 뜬다.
순서를 나누는 기준이 없다고 한다. qs에 메소드를 붙여서 정렬 순서를 정해주자.
qs = models.Room.objects.filter(**filter_args).order_by("created")
이제 안뜬다.