갈루아의 반서재

HTML <form> 을 포함할 수 있도록 설문 상세 템플릿을 수정해보자.



/root/mysite/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>


- 위의 템플릿에서는 각각의 답변에 대해 라디오 버튼을 적용했다. 각각의 라디오 버튼은 설문 보기 ID와 연관되어 있다. 각각의 라디오 버튼의 이름은 "choice"로, 이것은 어떤 사람이 라디오 버튼 중 하나를 선택했고, 그 폼을 전송한다, 그러면 choice=# (선택된 보기의 ID) 라는 데이터를 전송한다. 이것이 가장 기본적인 HTML 폼이다. 


- 그리고 상기 폼의 액션을 {% url 'polls:vote' question.id %} 로 설정하고, post 방식으로 전송했다. post 방식의 전송(get 방식과 상반됨)을 쓴다는 것은 아주 중요한데, 왜냐하면 상기 폼의 전송은 서버측의 데이터를 변경하기 때문이다. 서버측의 데이터 변경시에는 전송방식을 post 로 해야한다. 이것은 장고에만 국한된 이야기는 아니다. 


- forloop.counter 는 해당 태그가 몇 번 루프를 통과했는지 보여준다.


- 내부 URLs 을 향하는 모든 POST 폼은 {% csrf_token %} 템플릿 태그를 이용하여야 한다. 


그러면 이제 제출된 데이터를 다루는 뷰를 만들어보자. 

앞서 이미 아래와 같은 URLconf 를 생성했음을 기억하자. 


/root/mysite/polls/urls.py

from django.conf.urls import url

from . import views


urlpatterns = [

    # ex: /polls/

    url(r'^$', views.index, name='index'),

    # ex: /polls/5/

    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name = 'detail' ),

    # ex: /polls/5/results/

    url(r'^(?P<question_id>[0-9]+)/results/$$', views.results, name='results'),

    # ex: /polls/5/vote/

    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote')

    

]



polls/views.py 파일에 아래와 같이 추가하자.



/root/mysite/polls/views.py:

from django.shortcuts import get_object_or_404, render

from django.http import HttpResponseRedirect, HttpResponse, Http404

from django.core.urlresolvers import reverse

from django.template import RequestContext, loader

from .models import Choice, Question

from gc import get_objects


def index(request):

    latest_question_list = Question.objects.order_by('-pub_date')[:5]

    context = {'latest_question_list': latest_question_list}

    return render(request, 'polls/index.html', context)


def detail(request, question_id):

    try:

        question = Question.objects.get(pk=question_id)

    except Question.DoesNotExist:

        raise Http404("Question does not exist")

    return render(request, 'polls/detail.html', {'question': question})


def results(request, question_id):

    response = "You're looking at the result of question %s."

    return HttpResponse(response % question_id)


def vote(request, question_id):

    p = get_object_or_404(Question, pk=question_id)

    try:

        selected_choice = p.choice_set.get(pk=request.POST['choice'])

    except (KeyError, Choice.DoesNotExist):

        # Redisplay the question voting form.

        return render(request, 'polls/detail.html', {

            'question': p,

            'error_message': "You didn't select a choice.",

        })

    else:

        selected_choice.votes += 1

        selected_choice.save()

        # Always return an HttpResponseRedirect after successfully dealing

        # with POST data. This prevents data from being posted twice if a

        # user hits the Back button.

        return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))


- request.POST은 제출한 데이터를 키 네임별로 접근하게 해주는 dictionary-like 오브젝트이다. 상기 예에서 request.POST['choice']는 선택한 보기의 ID를 string 형태로 반환해준다. request.POST 값은 항상 문자열이다. 


request.POST['choice']는 보기가 POST 데이터에 제공되지 않으면 KeyError를 일으킨다. 상기 코드는 키에러를 체크하고 보기값이 주어져 있지 않으면 다시 질문 폼으로 이동한다. 


- 해당 보기 카운트가 올라간 후에는 일반적인 HttpResponse가 아니라 HttpResponseRedirect를 반환한다. HttpResponseRedirect는 하나의 인수만 가지는데, 리다이렉트될 URL이 그것이다. 


- 위에서 HttpResponseRedirect 생성자에서 reverse() 함수를 사용하고 있다. 위의 예에서 reverse() 함수는 아래와 같은 문자열을 호출한다. 


'/polls/3/results/'


여기서 3 은 p.id 의 값이다. 그리고 이렇게 리다이렉티드된 URL은 최종 페이지를 표시하기 위해 'result' 뷰를 호출한다. 


어떤 사람이 투표를 마쳤다면, vote() 뷰는 해당 설문의 결과를 표시하는 페이지로 리다이렉트시켜준다. 해당 뷰를 작성해보자. 


/root/mysite/polls/views.py

from django.shortcuts import get_object_or_404, render

from django.http import HttpResponseRedirect, HttpResponse, Http404

from django.core.urlresolvers import reverse

from django.template import RequestContext, loader

from .models import Choice, Question

from gc import get_objects


def index(request):

    latest_question_list = Question.objects.order_by('-pub_date')[:5]

    context = {'latest_question_list': latest_question_list}

    return render(request, 'polls/index.html', context)


def detail(request, question_id):

    try:

        question = Question.objects.get(pk=question_id)

    except Question.DoesNotExist:

        raise Http404("Question does not exist")

    return render(request, 'polls/detail.html', {'question': question})


def results(request, question_id):

    question = get_object_or_404(Question, pk=question_id)

    return render(request, 'polls/results.html', {'question':question})


def vote(request, question_id):

    p = get_object_or_404(Question, pk=question_id)

    try:

        selected_choice = p.choice_set.get(pk=request.POST['choice'])

    except (KeyError, Choice.DoesNotExist):

        # Redisplay the question voting form.

        return render(request, 'polls/detail.html', {

            'question': p,

            'error_message': "You didn't select a choice.",

        })

    else:

        selected_choice.votes += 1

        selected_choice.save()

        # Always return an HttpResponseRedirect after successfully dealing

        # with POST data. This prevents data from being posted twice if a

        # user hits the Back button.

        return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))


그럼 이제 polls/results.html 템플릿을 만들자.


/root/mysite/polls/templates/polls/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>


그럼 이제 웹브라우저에서 /polls/1/ 으로 가서 투표를 해보자. 매번 투표할 때마다 카운트가 올라가야 하고, 값을 선택하지 않고 투표했을 경우 에러메시지가 표시되어야 한다. 

선택하지 않고 투표한 경우


매번 투표할 때마다 카운트가 올라가는 것을 볼 수 있다.