본문 바로가기

프로젝트/파이썬 장고를 이용한 웹페이지 만들기

8. 튜토리얼 따라하기 - 설문조사(3)

오늘은 투표기능이 동작하도록 간단한 폼을 만들어보겠다.

detail.html을 수정하고 vote 뷰에도 기능을 추가하자.

 

간단한 폼 만들기

polls/templates/polls/detail.html을 다음과 같이 수정하자.

    <h1>{{ question.question_text }}</h1>

    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

    <form action="{% url 'polls:vote' question.id %}" method="post">
        {% csrf_token %}
        {% for choice in question.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 태그는 사용자가 답변 항목을 선택하고 전달할 수 있도록 만든다. 사용자가 선택한 항목의 번호를 vote 뷰를 전달하도록 action 속성에 vote URL이 출력되게 url 템플릿 태그를 사용한다.
  • 위의 form 템플릿은 각 질문 선택 항목에 대한 라디오 버튼을 표시한다. 각 라디오 버튼의 value는 연관된 질문 선택 항목의 ID 이다. input type이 radio로 설정되어 있는 것을 확인할 수 있을 것이다.
  • method 속성에 써있는 post는 HTTP 메서드 중 하나이며 서버로 정보를 전달할 때 사용하는 일반적인 방법이다.
  • forloop.counter는 템플릿 문법에서 제공하는 기능 중 하나로  반복문의 반복 횟수를 출력해주는 기능을 한다.
  • csrf_token은 CSRF(Cross Site Request Forgeries) 공격을 막기 위한 수단 중 하나이다. 즉, 방금 서버로 들어온 요청이 사이트 내부에서 온 것이 맞는지 확인하는 용도로 csrf_token의 값을 사용한다. (POST form을 생성 시, csrf를 염두에 두었어야했다. 하지만, django에선 모든 post form은 {% csrf_token %}이라는 템플릿 태그를 사용하여 csrf로부터 보호해준다.)

이제, detail.html에서 만들어진 정보를 받을 수 있게 vote 뷰를 수정해보자. polls/views.py를 다음과 같이 수정하자.

def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        # Redisplay th question voting form
        return render(request, 'polls/detail.html', {'question': question,
                                                     'error_message': "You didn't select a choice,"})
    else:
        selected_choice.votes += 1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results', args=(question.id, )))

일단, request.POST[변수이름]을 통해 전달받은 변수의 값들을 확인할 수 있다. 즉, 전송된 자료에 접근할 수 있도록 해주는 사전과 같은 객체이다. 이 때 전달되는 값(request.POST)은 항상 문자열이기 때문에 문자열이라는 사실을 기억하자.

만약, 전달받은 답변이 해당 투표 항목에 없다면 다시 상세 페이지로 이동한다. 이 때 답변을 선택하지 않았다는 error message도 같이 전달한다.

반대로, 제대로된 답변을 선택한 것이라면 해당 답변의 답변 수를 1 증가 시키고 결과 화면으로 이동한다.


이제, 결과를 출력하는 result  뷰도 변경하자. polls/views.py를 다음과 같이 변경한다.

def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question': question})

polls/templates/polls/results.html도 만들어 준다. results.html 내의 코드는 다음과 같이 작성한다.

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

results.html은 각 답변 항목과 투표 수를 한꺼번에 보여준다.

 

 

 

다음 글에서는 제너릭 뷰를 사용해 보겠다.