实战Django:官方实例Part4
上一个part我们创建了投票的内容页,但这个页面仅仅局限于静态展示,投票的“投”字还无从体现。接下来,我们就来看一下,如何把票投起来。
19.创建表单
我们来更新模板文件polls/detail.html,在其中加入“form”元素:
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="投票" />
- </form>
简单讲解一下:
- 在上面这个模板中,我们给每一个投票项加入一个单选按钮,每个单选按钮都关联到投票项的编号,单选按钮的名字统一叫做“choice”,这样当有用户选中一个投票项并提交表单(即投票)的时候,它就会发送一组”choice=#”(#为编号)的数据,这是html表单的基本概念。
- 我们把表单的action设置为“{% url 'polls:vote' question.id %}”,把它的method设置为“post”,“post”意味着让用户把这数据提交给服务器端。
- forloop.counter显示了for循环的次数。
- 当我们使用POST方式来提交表单的时候,我们需要考虑跨站请求伪造的风险。不过不用担心,Django早就为我们考虑到了这一点,你只要在模板里加上“ {% csrf_token %} ”标签,就可以规避风险。
注意,别忘了将模板文件存为utf-8编码。
接下来,我们修改相应的视图,编辑polls/views.py文件,改成下面这样:
polls/views.py:
- from django.shortcuts import get_object_or_404, render
- from django.http import HttpResponseRedirect, HttpResponse
- from django.core.urlresolvers import reverse
- from 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):
- # 没有选中投票项的,重新显示投票表单.
- return render(request, 'polls/detail.html', {
- 'question': p,
- 'error_message': "You didn't select a choice.",
- })
- else:
- selected_choice.votes += 1
- selected_choice.save()
- # 在成功提交后,返回一个带有POST数据的HttpResponseRedirect 对象,
- # 可防止用户在使用返回命令时提交两次数据。
- return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))
讲解一下:
- request.POST是一个类似字典的对象,在本例中,你可以用request.POST['choice']来获得已选择的投票项的编号。
- 当用户提交投票时并没有选中任何投票项时,request.POST['choice']会抛出一个KeyError的异常,此时我们会重新显示投票表单。
- 投票成功时,当前选中的投票项的票数会自动加1,此后我们会返回一个HttpResponseRedirect对象(而不是之前我们用过的HttpResponse),这个HttpResponseRedirect只带一个参数,就是用户投票后我们想让用户看到的页面链接。
- 在HttpResponseRedirect中我们使用了reverse() 函数,我们用这个函数来生成上面讲到的页面链接,在本例中,这个reverse() 返回的字符会象下面这个样子:
- '/polls/3/results/'
上面的3就来自于“p.id”这个参数,这个重定向链接会调用”results”视图来显示最终的页面。
当用户完成投票后,vote视图会将链接转到结果页,接下来,我们就来编写结果页的视图。
编辑polls/views.py文件:
polls/views.py:
- 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})
这个视图和我们前面介绍过的detail视图很象,只是模板名称不同而已。
然后我们给这个视图配上相应的模板。创建一个模板文件polls/results.html,加入以下内容:
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 %}">再投一次?</a>
下面,让我们访问链接http://127.0.0.1:8000/polls/1/,并开始投票:
成功投票之后,我们可以看到页面自动跳转到结果页,在这一页我们可以看到投票的数据:
你还可以试一下什么都不选的时候,点击投票按钮会有什么结果。
20.使用通用视图,精简代码
在Web开发中,我们追求“Less code is better”的理念。在Part3中的detail视图和这里的results视图非常相似,但代码略显冗余,可以进行精简。同样可精简的还有我们的index视图。
Django给我们提供了通用视图,我们可以在polls这个应用中使用通用视图,来把代码改造得更加简洁。
要使用通用视图,我们需要做以下操作:
- 转换url配置文件;
- 删除一些不需要的视图;
- 引入通用视图;
首先我们来调整url配置文件,编辑polls/urls.py文件,改成这样:
polls/urls.py:
- from django.conf.urls import patterns, url
- from polls import views
- urlpatterns = patterns('',
- url(r'^$', views.IndexView.as_view(), name='index'),
- url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
- url(r'^(?P<pk>\d+)/results/$', views.ResultsView.as_view(), name='results'),
- url(r'^(?P<question_id>\d+)/vote/$', views.vote, name='vote'),
- )
注意,url中的正则表达式发生了变化,第二、三个正则表达式中,我们把<question_id> 改成了<pk>。
下一步,我们把index,detail,results视图删除,换上Django的通用视图。编辑polls/views.py,把它改成下面这样:
polls/views.py:
- from django.shortcuts import get_object_or_404, render
- from django.http import HttpResponseRedirect
- from django.core.urlresolvers import reverse
- from django.views import generic
- from polls.models import Choice, Question
- class 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'
我们使用了两个通用视图,ListView和DetailView。前一个视图用列表的方式来显示一组对象,后一个视图则用来显示某一特定对象的详细内容。
- 每一个视图都需要知道它将对哪个模型进行操作,所以我们给它提供了model属性;
- DetailView需要从链接中获取主键值(pk),这也就是我们在urls.py中把<question_id> 改成<pk>的原因。
如果不给DetailView指定template_name属性的话,它将使用<应用名称>/<模型名称>_detail.html作为模板名称,在本例中,这个默认的模板名会是这个样子:polls/question_detail.html。因为我们的detail视图和results视图都使用了DetailView,所以要给它们分别指定template_name,以便用不同的模板来呈现各自的内容。
与此类似的是,如果不给ListView指定template_name属性,它将使用<应用名称>/<模型名称>_list.html作为模板名称,我们给index视图指定了template_name属性,让它使用我们已经做好的”polls/index.html“模板。
在Part3中,我们的视图向模板传递了question和latest_question_list变量,在DeltailView中,我们不需要传递question变量,它会自动将其发给模板。但是,在ListView中,它向模板自动提交的变量是question_list——这是一个完整的列表,而我们只需要提供最新的5个就足够了。我们希望用latest_question_list来代替默认的question_list,因此我们给模板提供了context_object_name属性。
做完上面这些,让我们再次在浏览器中查看我们的工作成果。
在Part5中,舍得将讲述如何测试我们的投票应用。
【未完待续】
本文版权归舍得学苑所有,欢迎转载,转载请注明作者和出处。谢谢!
作者:舍得
首发:舍得学苑@博客园
实战Django:官方实例Part4的更多相关文章
- 实战Django:官方实例Part1
[写在前面] 撰写这个实战系列的Django文章,是很久之前就有的想法,问题是手头实例太少,一旦开讲,恐有"无米下锅"之忧. 随着对Django学习的深入,渐渐有了些心得,把这些心 ...
- 实战Django:官方实例Part2
我们接着Part1部分往下讲.我们在part1中启动服务器后,并没有在管理页面中发现新添加的Polls应用,怎么办捏? 7.在管理界面中显示Question 只要注册一下这个应用就可以了.编辑poll ...
- 实战Django:官方实例Part6
我们终于迎来了官方实例的最后一个Part.在这一节中,舍得要向大家介绍Django的静态文件管理. 现在,我们要往这个投票应用里面添加一个CSS样式表和一张图片. 一个完整的网页文件,除了html文档 ...
- 微软官方实例 RazorPagesMovie 在 asp.net core 2.1 版本下的实战
微软官方实例 RazorPagesMovie 在 asp.net core 2.1 版本下的实战 友情提示: 操作系统: MacOS 10.13.5 dotnet core: version 2.1. ...
- 实战Django:简易博客Part1
舍得学习新技能的时候,通常不喜欢傻读书--捧着一本阐述该项技能的书籍,然后傻看,一路看下来,脑子里塞满了新的概念.知识点,头是越来越大,但技能却几乎没掌握半分. 多年来,舍得养成了用做实例来学习新技能 ...
- 对《[Unity官方实例教程 秘密行动] Unity官方教程《秘密行动》(十二) 角色移动》的一些笔记和个人补充,解决角色在地形上移动时穿透问题。
这里素材全是网上找的. 教程看这里: [Unity官方实例教程 秘密行动] Unity官方教程<秘密行动>(九) 角色初始设定 一.模型设置: 1.首先设置模型的动作无限循环. 不设置的话 ...
- Android实例-Delphi开发蓝牙官方实例解析(XE10+小米2+小米5)
相关资料:1.http://blog.csdn.net/laorenshen/article/details/411498032.http://www.cnblogs.com/findumars/p/ ...
- 微信应用号开发知识贮备之altjs官方实例初探
天地会珠海分舵注:随着微信应用号的呼之欲出,相信新一轮的APP变革即将发生.从获得微信应用号邀请的业内人士发出来的一张开发工具源码截图可以看到,reacjs及其相应的FLUX框架altjs很有可能会成 ...
- 源于《Unity官方实例教程 “Space Shooter”》思路分析及相应扩展
教程来源于:Unity官方实例教程 Space Shooter(一)-(五) http://www.jianshu.com/p/8cc3a2109d3b 一.经验总结 教程中步骤清晰,并且 ...
随机推荐
- Android——Dialog
public class DialogActivity extends Activity { //进度对话框 ProgressDialog progressDialog; @Override ...
- 利用sql批量删除表,存储过程
利用sql批量删除表,存储过程. 最近用godaddy的空间,由于系统里面的表多,一个个的删除很麻烦,就网上搜集了一下解决方法. 给大家分享一下: 1.批量删除存储过程 declare @procNa ...
- js对象4-js原型--杂志
提问:在js中什么是原型 prototype 每个学js的人都有自己的解释,网上一大堆的解释与应用,但是看了他们的解释表示还是不理解怎么办?(原因是他们说的太天花乱坠了) 官方手册解释:prototy ...
- FPGA中的时序分析(四)
常用约束语句说明 关于Fmax 上述是实现Fmax的计算公式,clock skew delay的计算如下图, 就是两个时钟的差值.到头来,影响Fmax的值的大小就是组合逻辑,而Fmax是针对 ...
- React生命周期和虚拟DOM
一.虚拟DOM 1.React并不直接操作DOM,React中的render方法,返回一个DOM描述,React能够将这个DOM描述与内存中的表现进行比较,然后以最快的方式更新浏览器 2.React实 ...
- Xcode去除某种类型的警告
首先来看下当有警告时,怎么找到警告类型,在某条警告上,右键—>Reveal in Log 下面 [ ] 中间就是警告信息 去除警告信息的几种方式: 一.使用编译器提供的宏 ...
- 学习记录 彻底搞清 C#中a++与++a的区别
首先 a++和++a 的定义:看个例子A: a=5; b=++a; // 相当于a=a+1;b=a; 结果是a=6,b=6B: a=5; b=a++; // 相当于b=a;a=a+1; 结果是a=6, ...
- Winserver2008R2 .netframework4.5 asp.netmvc 访问出现的是文件列表。
Winserver2008R2 .netframework4.5 asp.netmvc 访问出现的是文件列表,服务器需要安装如下的补丁,才可正常访问. http://www.microsoft.com ...
- hadoop2.5.1搭建(一)
1.1配置 1.1.1修改hosts vi /etc/hosts 192.168.220.64 cluster4 192.168.220.63 cluster3 1.2安装jdk rpm安装 rpm ...
- Ajax-(get/post/jQuery方式请求)
< !DOCTYPE html > < html xmlns = "http://www.w3.org/1999/xhtml" > < head &g ...