Write a simple form
polls.detail.html에 간단한 HTML <form>문을 추가하였다.
--------------
<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>
--------------
- 위의 템플릿은 각각의 질문 choice에 radio button을 보여준다.
# <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
각각의 버튼 값은 질문의 choice id 값과 연동된다.
=> 누군가 하나의 radio 버튼을 누르고 submit을 하면 POST data choice=#id로 보내진다.
(html 기본 form문의 방식)
- form의 action은 다음과 같이 지정하였다.
# <form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
method는 'post' 방식 => data server-side를 대체하기 위해서는 post 방식을 사용하여야 한다.
- forloop.counter는 얼마나 많은 for 티그가 loop 되는지를 나타냄
- POST form을 만들었기 때문에 Cross Site Request Forgeries를 신경써야함(??)
장고가 매우 쉬운 보호 시스템 제공
=> 모든 POST 폼들은 {% csrf_token %} 템플릿 태그로 쓰임(??)
이전 part 3에서 polls/urls.py에 vote url을 추가했었다.
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
이번에는 vote()의 실제 버전을 만들어보자
polls/views.py에서
-------
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse
from .models import Choice, Question
# ...
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 the 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()
# 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=(question.id,)))
-------
vote 기능만 바꿔준다.
튜토리얼에서 다루지 않았던 기능이 몇 개 추가되는데
- request.POST: dict과 비슷한 object- key name으로 데이터에 접근할 수 있게 해줌.
=> request.POST['choice']는 선택된 choice의 ID를 string으로 나타냄
또한 장고는 request.GET도 같은 방식으로 제공
- request.POST['choice'] => POST 데이터가 제공되지 않았다면 KeyError 발생
위 코드는 choice가 없으면 keyerror를 체크하고 메세지를 표시해줌
- choice가 카운트 된 이후, 코드는 HttpResonse 대신에 HttpResponseRedirect를 리턴
HttpResponseRedirect는 한개의 argument를 받음: 사용저가 redirected할 URL
tip: POST 데이터를 성공적으로 다룬 뒤에는 항상 HttpResponseRedirect를 사용해줘야한다!
- HttpResponseRedirect constructor에서 reverse() 함수를 사용
=> 하드코드 사용을 피해줌
=> view를 가르키는 이름을 주면 됨 (??)
(We are using the reverse() function in the HttpResponseRedirect constructor in this example. This function helps avoid having to hardcode a URL in the view function. It is given the name of the view that we want to pass control to and the variable portion of the URL pattern that points to that view. In this case, using the URLconf we set up in Tutorial 3, this reverse() call will return a string like)
이번 예시에서는
reverse('polls:result', args=(question.id,))는
'polls/1/results/' 가 불러온다. (만약 3이 question.id라면)
이 재설정된 URL은 results view를 페이지에 표시해준다.
누군가 질문에 투표하면, vote() 뷰는 results page를 표시해준다.
polls/views.py의 results 함수를 수정하자
--------
from django.shortcuts import get_object_or_404, render
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question': question})
--------
이는 튜토리얼 3에서의 detail() 뷰와 거의 동일하다. 다른건 템플릿 이름 (results.html) 뿐이다
이제 polls/results.html 템플릿을 만들자
polls/templates/polls/result.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>
----------
를 추가해준다.
이제 localhost:8000/polls/1/ 에 들어가서 투표를 실제로 해보자.
업데이트된 results 페이지가 나와야 한다.
만약 아무런 선택 없이 submit을 하면 에러가 뜬다.
Note
The code for our vote() view does have a small problem. It first gets the selected_choice object from the database, then computes the new value of votes, and then saves it back to the database. If two users of your website try to vote at exactly the same time, this might go wrong: The same value, let’s say 42, will be retrieved for votes. Then, for both users the new value of 43 is computed and saved, but 44 would be the expected value.
This is called a race condition. If you are interested, you can read Avoiding race conditions using F() to learn how you can solve this issue.
(https://docs.djangoproject.com/en/1.10/ref/models/expressions/#avoiding-race-conditions-using-f)
'backend > django' 카테고리의 다른 글
django tutorial: static files- 스타일시트, css, stylesheet (part 6) (0) | 2017.03.03 |
---|---|
django tutorial: Testing(테스팅) (part5) (0) | 2017.03.03 |
django tutorial: View, template 이용하기 (part3) (0) | 2017.03.02 |
django tutorial: API 사용해보기 & django admin 사용법 (part2) (0) | 2017.03.02 |
postgresql과 django 연동 (장고 데이터베이스 연동하기) (0) | 2017.03.01 |