民意调查Django实现(四)
2015-12-16 16:19
253 查看
我们接着上一小节的末尾开始学习,在上一个小节中,我们主要是了解了Django中的模板,即templates的使用。在这个小节中,我们主要关注于简单的表单处理并且裁剪我们的代码。
编写一个简单的表单
我们来更新一下我们的detail模板(“polls/detail.html”),添加HTML的
简单描述一下:
- 上面的模板为每一个question展示了一组单选按钮。每一个按钮的值(value)是相关问题的选项ID。每一个按钮的名称是”choice”。也就是意味着,当用户选择其中一个选项框并且提交表单的时候,将会发送post数据
- 我们设置表单的action为
- forloop.counter标示for标签在这个循环中出现的次数。
- 既然我们创建了一个POST表单(该表单可能会影响修改数据),我们就需要去担心通过站点请求的伪装。幸运的是,我们不必太过担心,因为Django提供了一个简单易用的系统来阻止他。简单来说,目标是内部URLs的所有的POST表单都应该使用
现在,我们来创建处理提交数据的视图。在上一个小节中,我们创建了包含下面这一行的URLconf。
polls/urls.py
我们也创造了一个vote()函数的临时实现。现在我们来真正使用起来。向polls/views.py中添加下面的代码:
该代码包含下面几个事情:
request.POST是一个类似于字典的对象,能够使我们通过键名称去访问提交的数据。在这个例子中,
注意,Django也提供了
如果在POST数据中没有提供choice选项,那么
在增加了选项数量之后,代码返回一个HttpResponseRedirect而不是一个正常的HttpResponse。HttpResponseRedirect使用一个单一的参数:也就是用户将要重定向的url地址。
在这个例子的HttpResponseRedirect结构中我们使用reverse()函数。这个函数帮助我们避免发生视图函数中硬编码一个URL地址。他被给予我们想要传递到的视图名称和指向视图的URL正则中的变量部分。在这个例子中,使用我们在上一小节中设置的URLconf,最终reverse()调用将会返回类似于下面的字符串:
在用户对相应问题投票之后,vote()视图将会重定向到这个问题的results页面。现在我们来重写这个页面:
下面我们来添加results模板:
创建一个polls/results.html模板:
polls/templates/polls/results.html
现在,我们在浏览器中进入/polls/1/中去为相应的问题去投票。每次投票后你都能看到相应的结果更新。如果你没有选择选项而点击提交的话,你将会看到错误信息。
下面来看一下我的运行结果:
使用通用视图:减少代码更好
detail()和results()视图是非常简单的 - 就像是上面提到的,太冗余了。展示投票列表的index()视图也是一样的。
这些视图代表着基本Web开发的通用例子:通过URL传来的参数从数据库获取数据,加载模板并且返回一个渲染模板。因为这些都是雷同的,所以Django提供了一个快捷方式,成为”通用视图”系统。
通用试图系统抽象除了通用正则,这使得我们可能不需要编写Python代码也能构件一个应用。
下面让我们的应用使用通用视图,所以我们可以删除一大些代码了。我们仅仅需要几步就能做出转换。
转换URLconf
删除一些旧的,不需要的视图
基于Django的通用视图引入新的视图
修改URLconf
首先,打开polls/urls.py并且像下面这样修改:
注意,正则匹配中间的
修改视图
下一步,我们将要移除我们旧的index,detail和results视图,然后使用Django的通用视图。打开polls/view.py文件,并且像下面这样修改:
我们使用了两个通用视图:ListView和DetailView。这两个视图抽象除了“显示对象列表”和“现象特定对象详细信息”的概念。
每一个通用视图都需要知道他们操作的模型是什么。这个通过
默认情况下,DetailView通用视图使用称为
相似的,ListView通用视图使用默认的模板,称为
在之前的小节中,模板已经被提供了context,也就是上下文,该上下文包含question和latest_question_list变量。对于DetailView,question已经被自动提供了。我们使用Django的模型(Question),所以Django能够决定为上下文变量使用一个近似的名称。然而,对于ListView,自动生成的上下文变量是question_list。为了重写,我们可以使用
下面我们来运行一下,看一下效果:
运行效果和上面的。
编写一个简单的表单
我们来更新一下我们的detail模板(“polls/detail.html”),添加HTML的
<form>元素。
[code]<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>
简单描述一下:
- 上面的模板为每一个question展示了一组单选按钮。每一个按钮的值(value)是相关问题的选项ID。每一个按钮的名称是”choice”。也就是意味着,当用户选择其中一个选项框并且提交表单的时候,将会发送post数据
choice=#,其中#就是对应的选择的选项的ID。这个是HTML表单的基本概念。
- 我们设置表单的action为
{% url 'polls:vote' question.id %},并且我们设置
method="post"。使用post方式(和使用get方式对应)是非常重要的,因为提交表单的行为将会改变服务端的数据。无论你什么时候创建能够修改服务端数据的表单的时候,一定要使用
methon="post"。这个技巧不是Django专有的。这个仅仅就是好的网页开发技巧。
- forloop.counter标示for标签在这个循环中出现的次数。
- 既然我们创建了一个POST表单(该表单可能会影响修改数据),我们就需要去担心通过站点请求的伪装。幸运的是,我们不必太过担心,因为Django提供了一个简单易用的系统来阻止他。简单来说,目标是内部URLs的所有的POST表单都应该使用
{% crsf_token %}模板标签。
现在,我们来创建处理提交数据的视图。在上一个小节中,我们创建了包含下面这一行的URLconf。
polls/urls.py
[code]url(r'^(?P<question_id>\d+)/vote/$',views.vote,name='vote'),
我们也创造了一个vote()函数的临时实现。现在我们来真正使用起来。向polls/views.py中添加下面的代码:
[code]# -*- coding:utf-8 -*- from django.http import HttpResponse,HttpResponseRedirect from django.shortcuts import get_object_or_404,render from django.core.urlresolvers import reverse from polls.models import 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.DoesNotExit): return render(request,'polls/detail.html',{ 'question':p, 'errot_message':"You did not 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']返回的是选中的选项的ID,这里是字符串。
request.POST值一直是字符串。
注意,Django也提供了
request.GET去访问GET数据的方式,但是对于
request.POST来说只有在代码中才能获取到相应的数据,这个大家可以去了解get方式请求和post方式请求的区别。
如果在POST数据中没有提供choice选项,那么
request.POST['choice']将会抛出KeyError异常。上面的代码检查KeyError异常,如果没有提供choice的话,重新显示带有错误信息的question表单。
在增加了选项数量之后,代码返回一个HttpResponseRedirect而不是一个正常的HttpResponse。HttpResponseRedirect使用一个单一的参数:也就是用户将要重定向的url地址。
在这个例子的HttpResponseRedirect结构中我们使用reverse()函数。这个函数帮助我们避免发生视图函数中硬编码一个URL地址。他被给予我们想要传递到的视图名称和指向视图的URL正则中的变量部分。在这个例子中,使用我们在上一小节中设置的URLconf,最终reverse()调用将会返回类似于下面的字符串:
'/polls/3/results',其中的3是
p.id的值。这个重定向URL然后调用’results’视图去展示最终的页面。
在用户对相应问题投票之后,vote()视图将会重定向到这个问题的results页面。现在我们来重写这个页面:
[code]def results(request, question_id): question = get_object_or_404(Question, pk=question_id) return render(request, 'polls/results.html', {'question': question})
下面我们来添加results模板:
创建一个polls/results.html模板:
polls/templates/polls/results.html
[code]<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>
现在,我们在浏览器中进入/polls/1/中去为相应的问题去投票。每次投票后你都能看到相应的结果更新。如果你没有选择选项而点击提交的话,你将会看到错误信息。
下面来看一下我的运行结果:
使用通用视图:减少代码更好
detail()和results()视图是非常简单的 - 就像是上面提到的,太冗余了。展示投票列表的index()视图也是一样的。
这些视图代表着基本Web开发的通用例子:通过URL传来的参数从数据库获取数据,加载模板并且返回一个渲染模板。因为这些都是雷同的,所以Django提供了一个快捷方式,成为”通用视图”系统。
通用试图系统抽象除了通用正则,这使得我们可能不需要编写Python代码也能构件一个应用。
下面让我们的应用使用通用视图,所以我们可以删除一大些代码了。我们仅仅需要几步就能做出转换。
转换URLconf
删除一些旧的,不需要的视图
基于Django的通用视图引入新的视图
修改URLconf
首先,打开polls/urls.py并且像下面这样修改:
[code]from django.conf.urls import patterns,url from polls import views urlpatterns = patterns('', # ex: /polls/ url(r'^$', views.IndexView.as_view(),name='index'), # ex: /polls/5/ url(r'^(?P<pk>\d+)/$',views.DetailView.as_view(),name='detail'), # ex: /polls/5/results url(r'^(?P<pk>\d+)/results/$',views.ResultsView.as_view(),name='results'), # ex: /polls/5/vote/ url(r'^(?P<question_id>\d+)/vote/$',views.vote,name='vote'), )
注意,正则匹配中间的
<question_id>改成了
<pk>。
修改视图
下一步,我们将要移除我们旧的index,detail和results视图,然后使用Django的通用视图。打开polls/view.py文件,并且像下面这样修改:
[code]# -*- coding:utf-8 -*- from django.http import HttpResponse,HttpResponseRedirect from django.shortcuts import get_object_or_404,render from django.core.urlresolvers import reverse from django.views import generic from polls.models import 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' def vote(request, question_id): # 和原来一样
我们使用了两个通用视图:ListView和DetailView。这两个视图抽象除了“显示对象列表”和“现象特定对象详细信息”的概念。
每一个通用视图都需要知道他们操作的模型是什么。这个通过
model属性提供
DetailView通用视图需要从URL中获取称为”pk”的主键,这就是为什么刚刚在urls.py中修改
question_id为
pk的原因了。
默认情况下,DetailView通用视图使用称为
<app name>/<model name>_detail.html作为模板。在我们的例子中,他会使用模板”polls/question_detail.html”。属性
template_name被用来告诉Django使用能够哪一个模板代替默认模板。我们也为results指定了template_name。
相似的,ListView通用视图使用默认的模板,称为
<app name>/<model name>_list.html,我们同样为他指定了我们自己的模板。
在之前的小节中,模板已经被提供了context,也就是上下文,该上下文包含question和latest_question_list变量。对于DetailView,question已经被自动提供了。我们使用Django的模型(Question),所以Django能够决定为上下文变量使用一个近似的名称。然而,对于ListView,自动生成的上下文变量是question_list。为了重写,我们可以使用
context_object_name属性,指定我们想要使用的
latest_question_list。这样的话,通过Django通用视图提供的属性,我们就可以指定我们想使用的变量了。
下面我们来运行一下,看一下效果:
运行效果和上面的。
相关文章推荐
- Hexagon SDK安装过程指导
- Google Chrome插件开发-Context Menus
- google Map的总结
- GIN+GORILLA=A GOLANG WEBSOCKET SERVER
- Ubuntu14.04安装ROS Indigo
- GoogleMap 获取自己的数字证书API key的步骤
- 对 Go 语言的综合评价
- google提供的Geocoding服务
- go语言中的接口类型与反射
- Google Chrome插件开发-Browser action
- 在golang中使用mgo多条件查询
- mongo常用语法
- 一个有关Golang变量作用域的坑
- django1.4 文档
- golang读取文本文件示例
- Determining if a point lies on the interior of a polygon
- 原 ajax.googleapis.com等公共库加载被“墙”的解决方法!
- Django restframework入门示例
- ZOJ 1045 HangOver
- django celery