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/ 으로 가서 투표를 해보자. 매번 투표할 때마다 카운트가 올라가야 하고, 값을 선택하지 않고 투표했을 경우 에러메시지가 표시되어야 한다.
선택하지 않고 투표한 경우
매번 투표할 때마다 카운트가 올라가는 것을 볼 수 있다.
'프로그래밍 Programming' 카테고리의 다른 글
[django] Writing our first test (0) | 2015.07.25 |
---|---|
[django] Use generic views: Less code is better (0) | 2015.07.18 |
[django] Namespacing URL names (0) | 2015.07.18 |
[django] Use the template system (0) | 2015.07.18 |
[django] Raising a 404 error (0) | 2015.07.18 |