KS blog

killins.egloos.com

포토로그



django 튜토리얼 #5/7 Form & Post by KillinS

* django 공식 홈페이지의 튜토리얼 작성법을 통해 간단한 설문조사 웹 프로젝트를 작성한다.
* 예제에서는 django에서 제공하는 개발용 웹서버를 이용하지만, 실제 서비스시에는 반드시 아파치와 같은 웹서버를 별도로 이용해야 한다.

1. POST를 이용한 데이터 송신

  - form과 같은 HTML에 의해 능동적으로 작성된 데이터를 다른 뷰로 던지는 방법중 하나는 post 방식을 이용하는 것이다.
    polls 앱에서 detail 뷰는 특정 항목에 실제로 투표를 하는 뷰이므로, 투표를 form을 통해 받고 결과를 result 뷰로 던지도록 수정한다.
    우선 detail 템플릿(detail.html)의 코드는 아래와 같다.

       <h1>{{ poll.question}}</h1>
       {% if error_message %} <p><strong>{{ error_message }}</strong></p> {% endif %}
       <form action="{% url 'polls:vote' poll.id %}" method="post">
       {% csrf_token %}
       {% for choice in poll.choice_set.all %}
           <input type="radio" name="choice" id="choice{{ forloop.counter}}" value="{{ choice.id }}" />
           <label for="choice{{ forloop.counter }}">{{ choice.choice_text }} </label><br />
       {% endfor %}
       <input type="submit" value="Vote" />
       </form>

  - 라디오 버튼을 이용해 투표를 입력받을 form을 하나 생성한다.
    결과가 전달될 뷰는 URLconfs에서 정의된 vote, 즉 views.vote() 함수이고, 방식은 POST이다.
    그리고 라디오 버튼을 생성하는데, 파라미터로 넘어온 poll에 해당되는 choice 객체만큼 루프를 돌면서 생성한다.
    결과가 전달될 페이지에서 사용할 라디오 버튼의 이름은 choice 이고, value는 각 choice 객체의 ID이다.
    만약 잘못된 입력이 있을 경우(라디오를 선택하지 않는 등) 현재 페이지에 error_messgae 파라미터를 넘겨줄 것이기 때문에
    최초에 해당 파라미터가 존재하는지 파악하여 화면에 뿌려준다.
    그리고 POST 방식을 사용하기 때문에 Cross Site Request Forgery에 대비해야한다.
    이것은 csrf_token을 추가함으로써 쉽게 해결할 수 있다. (모든 내부 URL에 대한 POST 방식의 링크는 이 구문을 넣어야 한다)

2. POST 방식의 데이터 수신

  - 장고 뷰 함수에서 POST 방식의 데이터를 수신하여 처리하는 방법은 static하게 파라미터를 받아 처리하는것과 큰 차이가 없다.
    request의 인자로 POST 데이터가 함께 전달되므로 이것을 처리하면 된다.
    예제인 polls 앱의 뷰 모듈(polls/views.py)의 vote 뷰 함수를 아래와 같이 수정한다.

      def vote(request, poll_id):
          p = get_object_or_404(Poll, pk=poll_id)
          try:
              selected_choice = p.choice_set.get(pk=request.POST['choice'])
          except (KeyError, Choice.DoesNotExist):
              return render(request, 'polls/detail.html', {
                  'poll' : p,
                  'error_message' : "you didn't select a choice",
              })
          else:
              selected_choice.votes += 1
              selected_choice.save()
              return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))

  - 우선 poll의 id가 파라미터로 넘어오기 때문에, 이것을 이용해서 poll 오브젝트를 얻어온다.
    그리고 request의 POST 데이터중에서 choice 값을 찾아 해당 값을 id로 갖는 choice 오브젝트를 얻어온다.
    request.POST는 딕셔너리와 비슷한 형태로서 데이터를 얻어올 때 key 값으로 value를 얻어올 수 있다.
    만약 키값이 없거나 choice 오브젝트를 찾지 못한다면 error_message 파라미터를 추가해서 다시 detail 템플릿으로 넘긴다.
    (위의 detail 템플릿에서 에러 메시지를 출력하도록 수정한 것을 참고)
    POST에 choice 정보가 있으면 해당 choice 오브젝트의 투표값을 1 증가시키고 DB에 저장한 뒤 result 뷰로 이동시킨다.
    여기서 중요한것은 HttpResponse가 아닌 HttpResponseRedirect를 사용한 것인데,
    이렇게 함으로써 결과 페이지에서 뒤로가기 버튼을 눌렀을때 data가 두번 post 되는것을 막을 수 있다.
    (모든 웹 애플리케이션에서 꼭 이것을 사용해야 함)
    그리고 HttpResponseRedirect()에서는 poll 앱의 URLconfs의 results 뷰로 이동시키기 위해 reverse()를 사용했다.
    reverse는 url()과 비슷한 함수로, 패턴자체, 함수 레퍼런스, URLconfs에 정의된 패턴명 등을 사용할 수 있다.
    위의 경우 URLconfs(polls/url.py)의 패턴명을 사용하여 reverse의 결과로 '/polls/n/results' 라는 스트링이 생성된다.
    따라서 브라우저는 poll의 id를 포함하고 results 뷰로 이동하게 된다.

  - 이제 results 뷰를 작성하면 된다.
    파라미터로 poll의 id가 전달되므로 해당 id의 poll 정보를 템플릿을 이용하여 화면에 보여준다.
    view.results()와 results.html은 각각 아래와 같다.

  - results 뷰 함수

        def results(request, poll_id):
            poll = get_object_or_404(Poll, pk=poll_id)
            return render(request, 'polls/results.html', {'poll':poll})

  - results 템플릿

        <h1> {{ poll.question }}</h1>
        <ul>
        {% for choice in poll.choice_set.all %}
            <li> {{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }} </li>
        {% endfor %}
        </ul>
        <a href = "{% url 'polls:detail' poll.id %}">Vote again?</a>



* 참고 : https://docs.djangoproject.com/en/1.5/intro/tutorial04/#write-a-simple-form