티스토리 뷰

Django

Django #6 장고의 Form

joyHong 2022. 3. 20. 00:27

장고의 form

 

웹 클라이언트에서 사용자의 입력을 서버로 전송하기 위해 자주 사용하는 방법으로 HTML form 이라는 것을 사용한다.

이번 글에서는 이 form을 어떻게 잘 사용할 수 있을지에 대해서 다루려고 한다.

먼저 장고에서 제공하는 기능 말고 순수하게 HTML을 이용해서 서버로 전송하고 결과를 응답받는 원시적인 방법부터 살펴보자.

 

지난 글에서처럼 새로운 앱인 v5 앱을 먼저 생성해야 한다.

터미널에서 앱을 생성 후 settings.py와 urls.py를 수정하는 것을 잊지 말도록 한다.

 

그럼 준비가 되었다는 가정하에 먼저 구성해 볼것은

127.0.0.1:8000/v5 로 요청이 들어오면 “이름”과 “취미”를 입력받아 서버로 전송하고 그 결과를 보내는 것을 해보도록 한다.

 

v5/urls.py

app_name = 'v5'


urlpatterns = [
    path('', IndexView.as_view(), name='index'),
]

 

요청이 들어오면 IndexView 라는 클래스형 함수에서 처리하도록 하였다.

IndexView에서는 지난 글에서 소개했던 TemplateView 제네릭 뷰를 상속받도록 구성한다.

이 뷰에서는 v5/index.html을 템플릿으로 사용하도록 한다.

 

v5/views.py

class IndexView(TemplateView):

    template_name = "v5/index.html"

 

이제 마지막으로 index.html 을 구성해야 하는데

그동안 다루지 않았던 템플릿 언어를 약간 다루면서 구성하려고 한다.

 

html을 크게 공통영역과 컨텐츠영역으로 구분하여 공통영역은 재사용하고  컨텐츠영역에 필요한 html만 들어가도록 구성하려고 한다.

공통영역은 base.html으로 생성하고, 컨텐츠 영역으로 첫번째 예제에서는 index.html로 생성하려고 한다.

 

templates/v5/base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Django Tutorial</title>
</head>
<body>
<div id="wrapper">
장고 5번째 튜토리얼 - Form
</div>
<div>
    {% block question %}
    {% endblock question %}

    {% block result %}
    {% endblock result %}
</div>
</body>
</html>

base.html은 기본 html 구성에 바디 안에 블록으로 구성했다. 

{% block 이름 %} ~~~ {% endblock 이름 %}  이런 형태이다.

여기서는 두개의 블록을 넣었는데 첫번째 question 이라는 곳에 index.html이 들어오게 할 것이다.

그리고 result 이라는 곳에 마지막에 생성할 result.html 이 들어오게 할 것이다.

 

다음으로 index.html을 구성한다.

 

templates/v5/index.html

{% extends "v5/base.html" %}

{% block question %}
    <p>
        정보를 입력해 보세요!
    </p>
    <form action="{% url 'v5:result' %}" method="POST">
        {% csrf_token %}
        <p>
            <label for="name">이름: </label>
            <input id="name" type="text" name="name">
        </p>
        <p>
            <label for="hobby">취미: </label>
            <input id="hobby" type="text" name="hobby">
        </p>
        <input type="submit" value="OK">
    </form>
{% endblock question %}

제일 먼저 첫줄에 extends 를 통해 v5/base.html 을 상속받는 것을 명시한다.

그 다음에 {% block question %} {% endblock question %} 안에 컨텐츠를 넣으면 된다.

컨텐츠의 내용은 HTML form을 이용하여 이름과 취미를 입력받아 submit 을 하면

POST 방식으로 정보를 전송하는데 이 때 전송하는 url은 v5:result 이라고 명시하였다.

그런데 우리는 아직 v5:result를 만든 적이 없다.

 

그럼 이 url로 요청이 들어오면 처리하는 로직을 생성하도록 하겠다.

 

v5/urls.py

app_name = 'v5'


urlpatterns = [
    path('', IndexView.as_view(), name='index'),
    path('result', ResultView.as_view(), name='result'),
]

 

v5앱의 urls.py에서 127.0.0.1:8000/v5/result 로 요청이 들어오면 ResultView에서 처리를 하도록 구성했다.

그런데 여기서 여태까지 설명하지 않았던 name 이라는 인자를 계속 생성하였다.

우리가 지금 앱을 만드는 방식처럼 여러개의 앱을 만들다며 보면 서로 다른 앱에서 동일한 뷰 이름이 사용될 수 있다.

이럴 때 혼동되는 것을 방지하기 위해 name을 사용한다. 

html form에서 url이 “v5:result” 라 사용했는데 이는 v5 앱의 result 라는 것을 뜻하게 된다.

그동안 v4, v3에서 뷰를 생성할 때 index 라는 이름을 계속 사용했음에도 충돌이 나지 않고 잘 찾아갈 수 있었던 이유가 여기에 있다.

 

다시 본론으로 돌아와 이제 ResultView를 생성하도록 한다.

 

v5/views.py

class ResultView(View):
    template_name = "v5/result.html"

    def post(self, request, *args, **kwargs):
        result = {
            'name': request.POST['name'],
            'hobby': request.POST['hobby'],
        }
        return render(request, self.template_name, result)

ResultView는 View 제네릭 뷰를 상속받고 POST 방식에 대한 처리를 구현하였다.

템플릿은 result.html로 하였다. 따라서 post 방식으로 요청이 들어오면 form에서 입력한 이름과 취미 값을 result에 담아 렌더링을 하게 된다.

응답 결과로 사용자가 입력한 값을 그대로 보여주는 result.html을 생성할 차례이다.

 

v5/result.html

{% extends "v5/base.html" %}

{% block result %}
    <p>
        입력 결과는 아래와 같습니다.
    </p>
    <li>이름 : {{ name }}</li>
    <li>취미 : {{ hobby }}</li>
    <p>
        <a href="{% url 'v5:index' %}">돌아가기</a>
    </p>
{% endblock result %}

 

index.html을 구성했던 것 처럼 base.html을 확장하고 블록 안에 결과 페이지를 구성하도록 한다.

 

여기까지 생성이 완료되었다면 http://127.0.0.1:8000/v5/ 로 접속하여 실행해보도록 한다.

결과 화면

 

위와 같이 우리는 제네릭 뷰를 활용하는 방법을 통해 서버로 입력값을 전송하고 응답을 받도록 구성하였다.

그럼 django 프레임워크에서 제공하는 더 간단한 방법을 활용해도록 하겠다.

 

Django에서는 Form 클래스 라는 것을 제공한다. HTML에서 사용하는 form을 장고에서는 Form 클래스를 통해 간단하게 활용이 가능하다. 

Form 클래스는 form의 field, field 배치, 디스플레이 widget, 라벨, 초기값, 유효성 체크 등을 기능을 수행한다. 또한 HTML 화면에서 미리 정의된 포맷(테이블, 리스트 등) 의 템플릿으로 폼의 내용을 렌더링하는 method를 제공한다.

 

Django Form의 주요 기능은 다음과 같다.

  1. 사용자가 처음으로 폼을 요청할 때 기본 폼을 보여준다.
  2. 제출 요청으로 부터 데이타를 수집하고 그것을 폼에 결합한다.
  3. 데이타를 다듬어서 유효성을 검증한다.
  4. 입력된 어떤 데이타가 유효하지 않다면, 폼을 다시 표시하는데 이번에는 초기값이 아니라 유저가 입력한 데이타와 문제가 있는 필드의 에러 메시지와 함께 표시한다.
  5. 입력된 모든 데이타가 유효하다면, 요청된 동작을 수행한다. 
  6. 일단 모든 작업이 완료되었다면, 사용자를 새로운 페이지로 보낸다.

 

 

Form 클래스를 사용하기 위해서는 사용하고자하는 애플리케이션의 디렉토리 내에 forms.py 파일로 생성해야 한다. 

 

그럼 v5앱 아래에 forms.py를 생성하도록 한다.

 

v5/forms.py

from django import forms


def validator_min_length(value):
    if len(value) < 3:
        raise forms.ValidationError('최소 글자는 3자 입니다.')


class MyForm(forms.Form):
    name = forms.CharField(max_length=10)
    hobby = forms.CharField(help_text="취미를 입력해주세요.", validators=[validator_min_length])

 

MyForm이라는 클래스를 생성하고 Form 클래스를 상속받았다.

그리고 두개의 필드(name, hobby)를 생성하였다. name 필드는 최대값이 10인 문자열 필드이고, hobby 필드는 안내문구로 ‘취미를 입력해주세요’ 로 설정하였고 유효성 체크를 최소 글자 3자로 설정하였다.

 

다음에는 URL Conf 설정을 해보록 하겠다.

 

v5/urls.py

urlpatterns = [
    path('', IndexView.as_view(), name='index'),
    path('result', ResultView.as_view(), name='result'),
    path('basic_form', BasicFormView.as_view(), name='basic_form'),
    path('done', DoneView.as_view(), name='done'),
]

 

Form클래스를 사용하는 예시를 위해 ‘v5/basic_form’으로 요청이 들어오면 BaseFormView 에서 처리를 하도록 하였고, 전송 결과는 DoneView 에서 처리하도록 설정하였다.

 

그럼 views.py에서 위의 2개에 해당하는 뷰를 만들어주겠다.

 

v5/views.py

class BasicFormView(FormView):
    template_name = 'v5/basic_form.html'
    form_class = MyForm
    success_url = 'done'


    def form_valid(self, form):
        return super().form_valid(form)


class DoneView(TemplateView):
    template_name = "v5/done.html"

 

BasicFormView는 FormView 클래스를 상속받았고, 템플릿을 'v5/basic_form.html' 로 지정하였다. form_class를 통해 사용하는 Form은 MyForm 으로 지정하였으며, 유효성 검증이 된 결과는 success_url을 통해 ‘v5/done’ 으로 요청을 보내게 된다.

클라이언트 화면에서 form을 입력하고 submit을 누르게 되면 내부적인 동작을 통해  Form 클래스에 정보가 담기게 되고 view로 넘어와 form_valid()를 통해 유효한 경우에 get_success_url() 를 호출하게 되고, get_success_url()는 success_url을 리턴을 함으로서  ‘v5/done’으로 요청이 가게 되는 것이다.

 

마지막 단계인 두개의 html을 작성해 보겠다.

basic_form.html 에서는 <form> 태그 안에 {{ form.as_p }} 형태로 지정해 주면 MyForm 클래스에서 생성한 필드가 <p> 태그 형태로 나타나게 된다. 그리고 submit 버튼을 넣어주면 된다.

 

templates/v5/basic_form.html

{% extends "v5/base.html" %}

{% block question %}
    <p>
        Basic Form 정보를 입력해 보세요!
    </p>
    <div>
        <form method="POST">
            {% csrf_token %}
            {{ form.as_p }}
            <button type="submit">OK</button>
        </form>
   </div>
{% endblock question %}

 

 

done.html 에서는 그냥 단순히 정보가 입력되었다는 문구만 보여주도록 생성하도록 한다.

 

templates/v5/done.html

{% extends "v5/base.html" %}

{% block question %}
    <p>
        정보가 입력되었습니다.
    </p>

    <p>
        <a href="{% url 'v5:basic_form' %}">돌아가기</a>
    </p>
{% endblock question %}

 

모든 준비가 완료되었으면 http://127.0.0.1:8000/v5/basic_form 로 접속을 해보도록 한다.

 

이렇게 Django의 Form클래스를 활용하여 간편하게 form을 사용하는 방법을 살펴보았다.

지금까지는 MVT 패턴 중에 View와 Template에 대해서만 다루어 보았다. 즉 Model 부분은 언급하지 않았기에 데이터베이스 연동없이 구성이 이루어졌다. 

다음 글에서는 Model 부분을 추가적으로 활용할 수 있도록 Model 클래스를 살펴보도록 하겠다.

 

coming soon…

 

 

이 글에서 사용한 코드는 아래의 깃에 있습니다.

https://github.com/joyhong85/Tutorial

'Django' 카테고리의 다른 글

Django #5 장고의 Views  (0) 2022.03.15
Django #4 홈페이지 만들기 feat.부트스트랩  (0) 2022.03.15
Django #3 Template 사용하기  (0) 2022.03.15
Django #2 간단한 웹페이지 만들기  (0) 2022.03.15
Django #1 시작하기  (0) 2022.03.15
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함