Writing your first Django app, part 4 编写你的第一个Django程序,第四部分
This tutorial begins where Tutorial 3 left off. We’re continuing the Web-poll application and will focus on simple form processing and cutting down our code.
本文继续第三部分讨论的内容。我们会继续开发网络投票程序并将深入研究简单的表单处理和缩减代码。
Write a simple form
编写一个简单的表单
Let’s update our poll detail template (“polls/detail.html”) from the last tutorial, so that the template contains an HTML <form> element:
现在把上一部分中提到的polls/detail.html做一下修改,在模板代码中加入<form>标签:
<h1>{{ poll.question }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="/polls/{{ poll.id }}/vote/" method="post">
{% for choice in poll.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice }}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>
A quick rundown:
- The above template displays a radio button for each poll choice. The value of each radio button is the associated poll choice's ID. The name of each radio button is "choice". That means, when somebody selects one of the radio buttons and submits the form, it'll send the POST data choice=3. This is HTML Forms 101.
- We set the form's action to /polls/{{ poll.id }}/vote/, and we set method="post". Using method="post" (as opposed to method="get") is very important, because the act of submitting this form will alter data server-side. Whenever you create a form that alters data server-side, use method="post". This tip isn't specific to Django; it's just good Web development practice.
- forloop.counter indicates how many times the for tag has gone through its loop
总结一下:
上面的模板会给每个投票的答案选项设置一个单选框。每个单选框的值对应Choice对象的ID字段;名称对应Choice对象的choice字段。这就是说,当有人勾选了单选框并提交时,浏览器会提交POST数据choice=3。这就是HTML的基本法则。
Now, let's create a Django view that handles the submitted data and does something with it. Remember, in Tutorial 3, we created a URLconf for the polls application that includes this line:
现在创建个Django视图来处理提交的数据。在第三部分中,我们的URLconf设置有下面的内容:
(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
So let's create a vote() function in mysite/polls/views.py:
现在我们在mysite/polls/views.py中创建vote()视图:
from django.shortcuts import get_object_or_404, render_to_response
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from mysite.polls.models import Choice, Poll
# ...
def vote(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id)
try:
selected_choice = p.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# Redisplay the poll voting form.
return render_to_response('polls/detail.html', {
'poll': 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('mysite.polls.views.results', args=(p.id,)))
This code includes a few things we haven't covered yet in this tutorial:
这段代码有些内容我们目前还没有提及到:
- request.POST is a dictionary-like object that lets you access submitted data by key name. In this case, request.POST['choice'] returns the ID of the selected choice, as a string. request.POST values are always strings.
Note that Django also provides request.GET for accessing GET data in the same way -- but we're explicitly using request.POST in our code, to ensure that data is only altered via a POST call.
request.POST是一个类似于字典的对象,你可以用名称索引来引用提交的数据。这种情况下,request.POST['choice']会以字符串的形式返回被选中的投票选项的ID。request.POST的值永远都是字符串。
同样在Django里,也可以用同样的方法从request.GET中获取GET数据。但是在这里我们使用request.POST,是因为为了保证数据是从POST方法提交过来的。
- request.POST['choice'] will raise KeyError if choice wasn't provided in POST data. The above code checks for KeyError and redisplays the poll form with an error message if choice isn't given.
如果choice在POST数据中不存在,调用request.POST['choice']就会引发KeyError异常。上面的代码会检查是否触发KeyError异常,如果有异常就会重新显示提交表单和一段报错信息。
- After incrementing the choice count, the code returns an HttpResponseRedirect rather than a normal HttpResponse. HttpResponseRedirect takes a single argument: the URL to which the user will be redirected (see the following point for how we construct the URL in this case).
在增加了投票选项的统计数之后,最后返回了HttpResponseRedirect对象而不是HttpResponse对象。HttpResponseRedirect对象接收一个参数:跳转URL。(下面会说明怎么样构建URL)
As the Python comment above points out, you should always return an HttpResponseRedirect after successfully dealing with POST data. This tip isn't specific to Django; it's just good Web development practice.
上面的注释里说明,你应该在POST数据处理完成之后永远返回HttpResponseRedirect对象。这个技巧不仅仅适用于Django,任何Web开发上都应该这么做。
- 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
在这个例子中我们在构造HttpResponseRedirect对象时使用了reverse()函数。这个函数可以解决硬编码URL产生的问题。我们只要传入要跳转的视图的名称和视图函数的参数就可以了。比如根据第三部分的URLconf设置,reverse()函数会返回下面的字符串:
'/polls/3/results/'
... where the 3 is the value of p.id. This redirected URL will then call the 'results' view to display the final page. Note that you need to use the full name of the view here (including the prefix).
这里的3就是p.id的值。跳转后的URL会调用results视图函数并显示最终的页面。在这里你要使用视图函数的全名(包括前缀)。
As mentioned in Tutorial 3, request is a HttpRequest object. For more on HttpRequest objects, see the request and response documentation.
在第三部分中提到,request是一个HttpRequest对象。要了解HttpRequest的更多内容,请参考文档request and response documentation。
After somebody votes in a poll, the vote() view redirects to the results page for the poll. Let's write that view:
有人提交投票之后,vote()视图会跳转到对应的结果页面。现在来编写对应的视图:
def results(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id)
return render_to_response('polls/results.html', {'poll': p})
This is almost exactly the same as the detail() view from Tutorial 3. The only difference is the template name. We'll fix this redundancy later.
这跟第三部分中的detail()视图基本上一模一样。唯一的区别就是模板名称。在后面我们会解决这个问题。
Now, create a results.html template:
现在创建results.html模板:
<h1>{{ poll.question }}</h1>
<ul>
{% for choice in poll.choice_set.all %}
<li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
Now, go to /polls/1/ in your browser and vote in the poll. You should see a results page that gets updated each time you vote. If you submit the form without having chosen a choice, you should see the error message.
现在访问/polls/1/并完成投票,你就能看到结果页面了,而且每次投票后都能看到数据有更新。如果没有勾选任何选项的话,你会看到错误信息。