12.-2 Forms API

조재훈·2022년 7월 27일
0

Clone_Airbnb

목록 보기
29/31
post-thumbnail

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")

이제 안뜬다.

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

0개의 댓글