参考:https://docs.djangoproject.com/en/1.9/intro/tutorial04/
我们现在来练习更多的views相关的表单处理方式。下面来更新一下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%}
<p>
<input type="radio" name="choice" id="choice-{{forloop.counter}}" value="{{choice.id}}" />
<label for="choice-{{forloop.counter}}">{{choice.choice_text}}</lable>
</p>
{%endfor%}
<p><input type="submit" value="Vote" /></p>
</form>
我们在这里对question的每个choice捡了一个input radio选单。我们设置了action为{%url 'polls:vote' question.id%},原理同上一节;forloop.counter是for循环的计数器,十分好用;csrf_token是django提供的一个跨站检查的token,使你无需担心跨站伪请求,只需要在form中调用{%crsf_token%}即可。本地站内的表单都推荐使用此方法。
OK,既然是提交到vote页面,那我们先来修改polls/views.py里的vote部分:
from django.shortcuts import render, get_object_or_404
from django.http import Http404
from django.http import HttpResponse, HttpResponseRedirect
from .models import Question
from django.core.urlresolvers import reverse
......
def vote(request, question_id):
#return HttpResponse("You're voting on question %s." % 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 did not 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,)))
- request.POST: 一个保存请求中POST来的信息的dict,里边的信息都默认被保存为字符串。同理还有request.GET。
- request.POST['choice']会报KeyError如果post信息里不包含choice。代码中检测这个KeyError,并返回显示之前的页面,并带着error_message信息。
- 如果顺利获取到choice,那么记录这个votes的数量+1,之后返回调用了HttpResponseRedirect方法。这个方法需要一个参数:用户会跳转到的url,一会儿我们会详细说明。顺带提及,正确处理完POST参数后,应该总是会调用HttpResponseRedirect,这是一个良好的网页设计习惯。
- 我们在HttpResponseRedirect方法中调用了reverse()方法来创建url。这个函数避免了我们写死url地址,而是用了我们urls.py保存的样式,利用一个view名称(包括namespace)和相应参数来创建需要的url。我们在polls/urls.py中定义的是:
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
那么这里我们获得的url就是形如
'/polls/3/results/'
的字串。
关于更多的request的用法,请参考 https://docs.djangoproject.com/en/1.9/ref/request-response/ (HttpRequest对象)
下面来修改原来的detail view:
def results(request, question_id):
# response = "You're looking at the results of question %s."
# return HttpResponse(response % question_id)
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question':question})
之后新建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>
大功告成,我们来尝试一下:
首先打开Question首页:
点击question,进入
已经可以点选投票了,点击”not much"选框,点击vote之后:
OK,投票结果已经记录了。可以点击vote again返回再投,可以看到result界面中的计数在增长。
返回投票页面,如果不选择直接提交,应该会走到我们的错误处理流程中:
唉唉?好像哪里不对。仔细一看发现原来是在views.py中忘了import Choice。赶紧填上,再次运行:
完毕。