这份指南接着继续讲解。我们将继续进行我们的Web-poll应用,并将注意力放在简单的表单处理及精简我们的代码。
编写一个简单的表单
让我们更新我们前一篇指南中的detail 模板(“polls/detail.html”),以使得那个模板包含一个HTML<form>元素:
{ { question.question_text }}
{% if error_message %}{ { error_message }}
{% endif %}
一个简单的介绍:
- 上面的模板为每一个question选项显示了一个radio button。每个radio button的值是其关联的question选项的ID。每个radio button的名字是"choice"。那即是说,当有人选择了radio buttons中的一个并提交了表单时,它将发送POST数据choice=#,其中#是选中的选项的ID。这是HTML表单的基本概念。
- 我们把表单的action设置为{% url 'polls:vote' question.id %},并设置method="post"。使用method="post" (而不是想法使用method="get")是非常重要的,因为提交这个表单的行为将改变服务器端的数据。无论何时你创建了一个表单,其改变了服务器端的数据,请都使用method="post"。这个建议不仅仅适用于Django;它只是一个好的Web开发实践。
- forloop.counter指示标签执行了多少次循环体。
- 由于我们在创建一个POST表单(它的执行可能会修改数据),我们需要考虑Cross Site Request Forgeries的问题。谢天谢地,你不需要为此太过担忧,因为Django有一个非常易于使用的系统来做保护。简单的说就是,目标为内部URLs的所有POST表单都应该使用模板标签。
现在让我们创建一个Django view来处理提交的数据,并用它来做点事情。记住,在中,我们为polls应用创建了一个URLconf,其包含这一行:
url(r'^(?P\d+)/vote/$', views.vote, name='vote'),
我们也创建了一个vote()函数的dummy实现。让我们创建一个真实的版本。给polls/views.py添加如下的内容:
from django.shortcuts import get_object_or_404, renderfrom django.http import HttpResponseRedirect, HttpResponsefrom django.core.urlresolvers import reversefrom polls.models import Choice, 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,)))
这份代码包含了一些在这份文档中我们之前没有提到过的东西:
-
是一个类字典对象,它使我们能够通过key名字来访问提交的数据。这个case里,request.POST['choice']以一个字符串的形式返回所选择的choice的ID。值总是字符串。
注意Django也提供了以相同的方式来访问GET数据 —— 但在我们的代码中我们显式地使用,来确保数据只通过一个POST调用改变。
-
如果在POST数据中没有提供choice,request.POST['choice']将激起。上面的代码检查了,并在没有提供choice时以一条error消息重新显示question表单。
-
在增加了choice计数之后,代码返回一个而不是一个普通的。接收一个单独的参数:用户将被重定向到的URL(参考下面的说明来了解,我们在这个case中如何构造URL)。
如同上面的Python注释中指出的,你应该总是在成功处理了POST数据之后返回一个。这个提示不特别用于Django;它只是一个很好的Web开发实践。
-
这个例子中,我们在构造函数里使用了函数。这个函数帮助避免在view函数中硬编码一个URL。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.在这个case中,使用了我们在Tutorial 3中设置的URLconf,这个调用会返回一个如下这样的字符串:
'/polls/3/results/'
-
... 其中3是p.id的值。这个重定向的URL将在随后调用'results' view来显示最后的页面。
如同在Tutorial 3中提到的,request是一个对象。要获取更多关于对象的信息,请参考。
有人在一个question中投票之后,vote() view会将那个question重定向到results页面。让我们来编写那个view:
from django.shortcuts import get_object_or_404, renderdef results(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/results.html', {'question': question})
这个函数与中的detail() view几乎完全一样。仅有的不同就是模板的名字。我们将会在后面解决这个冗余。
现在,创建一个polls/results.html模板:
{ { question.question_text }}
- {% for choice in question.choice_set.all %}
- { { choice.choice_text }} -- { { choice.votes }} vote{ { choice.votes|pluralize }} {% endfor %}
现在在你的浏览器中跳转到/polls/1/,并在question中投票。你应该看到了一个results页面,并且每次在你投票之后它都会得到更新。如果你提交了表单而没有选择一个choice,你应该看到error消息。
使用generic views:代码越少越好
detail() (来自与)和results() views都有点太简单了——而且如上面提到的,有些冗余。index() view (也来自于Tutorial 3),它显示一个polls的列表,也类似。
这些views展示了基本Web开发的一个常见场景:根据在URL中传进来的一个参数从数据库获得数据,加载一个模板,并返回rendered的模板。由于这是如此的常见,Django提供了一个快捷方式,称为“generic views”系统。
.Generic views抽象了通用的模式,使得你甚至不需要编写Python code就写出一个app。
让我们转换我们的poll app来使用generic views系统,我们将因此而能够删除大量我们自己的代码。我们只需要做到下面的几步就可以完成转换了。我们将:
- 转换URLconf。
- 删除一些老的,不需要的views。
- 引入新的基于Django的generic views的views。
Read on for details.
Why the code-shuffle?
通常,当你编写一个Django app时,你需要评估对于你要解决的问题而言,generic views是否是一个好方法,然后在一开始就使用它们,而不是做到了一半来重构。但这份指南直到现在一直聚焦于以“the hard way”编写views,来集中于核心概念。
在你开始使用一个计算器之前你应该先了解基本的数据知识。
修改URLconf
首先,打开polls/urls.py URLconf,然后像下面这样修改它:
from django.conf.urls import patterns, urlfrom polls import viewsurlpatterns = patterns('', url(r'^$', views.IndexView.as_view(), name='index'), url(r'^(?P\d+)/$', views.DetailView.as_view(), name='detail'), url(r'^(?P \d+)/results/$', views.ResultsView.as_view(), name='results'), url(r'^(?P \d+)/vote/$', views.vote, name='vote'),)
注意,第二和第三个模式的正则表达式匹配的模式的name已经由<question_id>变为了<pk>。
修改views
接下来,我们将移除我们老的index,detail和results views,并使用Django的generic views来替换。要做到这一点,打开polls/views.py文件,并像这样来修改它:
from django.shortcuts import get_object_or_404, renderfrom django.http import HttpResponseRedirectfrom django.core.urlresolvers import reversefrom django.views import genericfrom polls.models import Choice, Questionclass IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_question_list' def get_queryset(self): """Return the last five published questions.""" return Question.objects.order_by('-pub_date')[:5]class DetailView(generic.DetailView): model = Question template_name = 'polls/detail.html'class ResultsView(generic.DetailView): model = Question template_name = 'polls/results.html'def vote(request, question_id): ... # same as above
在这里我们使用了两个generic views:和。那两个views分别抽象了“显示一个对象的列表”和“为一个特定类型的对象显示一个详细页面”的概念。
- 每个generic view需要知道它的行为将作用于的模型(model)。这通过使用model属性来提供。
- generic view期望从URL里面称为“pk”的参数中抓取主键值,因而我们为generic views将question_id改为pk。
默认情况下, generic view使用一个称为<app name>/<model name>_detail.html的模板。在我们的例子中,它将使用模板"polls/question_detail.html"。template_name属性被用于告诉Django,要使用一个特定的模板名字来代替自动生成的默认的模板名字。我们也为results list view指定了template_name – 这确保results view和detail view在rendered时有一个不同的外观,尽管在场景背后它们都是一个。
类似地, generic view使用了一个默认的称为<app name>/<model name>_list.html的模板;我们使用template_name告诉来使用我们已有的"polls/index.html"模板。
在这份指南前面的部分,已经通过一个包含有question和latest_question_list context变量的context来提供了模板。对于DetailView,question变量是自动提供地 – 由于我们使用了一个Django model(Question),Django能够为context变量确定一个适当的名称。然而,对于ListView,自动生成的context变量是question_list。要覆写这一点,我们要提供context_object_name属性,指定我们想要用latest_question_list来替换。作为一个可选的方法,你可以修改你的templates来匹配新的默认的context变量 – 但是只告诉Django去使用你想使用的变量要简单得多。
运行服务器,并使用你的基于generic views的新polling app。
关于generic views的完整的细节,请参考。
当你熟悉了表单和generic views,就可以阅读,来学习测试我们的polls app的东西了。
原文地址:https://docs.djangoproject.com/en/1.7/intro/tutorial04/