写一个简单的表单
更新detail.html,代码:
<h1>{{ poll.question }}</h1> {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} <form action="{% url 'polls:vote' poll.id %}" method="post"> {% csrf_token %} {% 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_text }}</label><br /> {% endfor %} <input type="submit" value="Vote" /> </form>
解释一下上面的代码:
1 上面代码为poll对象的每个choice对应一个单选按钮,每个单选按钮的value就是choice的id号,name为choice,也就是说,按下提交按钮,发送的POST数据是这样的choice=3.
2 设置表单的action为{% url 'polls:vote' poll.id %},method为post.如果需要传递数据的话建议使用post方法.
3 forloop.counter 是for循环计数器
4 这里使用了POST表单,存在跨站点请求伪造的安全问题.值得庆幸的是,不需要你为此担心,django自带一个简单易用的拦截系统,那就是,所有的POST表单都应该使用{% csrf_token %}模板标签.现在,创建一个django试图可以处理这个提交上去的数据.
还记得在polls/urls.py中,这条语句吗?
url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),
现在创建一个真正的投票功能试图vote(),编辑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, 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(request, '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('polls:results', args=(p.id,)))
以上代码包含一些内容在之前没有提到过的:
1 request.POST是一个类似与字典的对象,可以通过键名访问到提交的数据,在这里request.POST['choice']返回被选择的choice的id.
注意:django同样也提供了获取get方法提交的数据的request.GET.
2 如果使用的键名不存在,会抛出KeyError的异常,上面的代码已经对这种异常进行处理了:重定向到提交数据的页面,同时显示错误信息.
3 增加相应choice的votes值之后,保存.并返回一个HttpResponseRedirect,而不是HttpResponse,这样会把显示页面重定向到第一个参数指定的url那里.
4 使用POST提交数据成功之后,都应该使用HttpResponseRedirect,这是web最佳实践,不仅仅适用于django.为什么?自己比较一下两者之间的不同吧
5 在HttpResponseRedirect中使用了reverse,这个函数避免的使用url硬编码,允许想模板中那样使用想polls:results这样的url表示方式.reverse返回的字符串类似这样的形式:
'/polls/3/results/'
这个3是p.id的值,这个请求url,会调用results视图来显示最终的页面.
更多关于HttpRequest对象的内容,阅读:https://docs.djangoproject.com/en/1.6/ref/request-response/
现在编辑results视图,代码:
from django.shortcuts import get_object_or_404, render def results(request, poll_id): poll = get_object_or_404(Poll, pk=poll_id) return render(request, 'polls/results.html', {'poll': poll})
以上代码几乎与detail视图,一模一样,稍后会解决这个冗余问题
现在,编辑results.html模板,代码:
<h1>{{ poll.question }}</h1> <ul> {% for choice in poll.choice_set.all %} <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li> {% endfor %} </ul> <a href="{% url 'polls:detail' poll.id %}">Vote again?</a>
现在访问/polls/1/,然后投票,你会看到每次投票results结果都要更新.如果你不选任何一个choice,然后提交,会看到错误信息.
使用通用模板,减少代码冗余
上面创建的detail(),results(),index()都是那么的相似.这些视图都是根据请求url从数据库中获取数据,然后加载一个模板,然后渲染之后的模板.因此django提供了"generic views"(下面称"通用视图")系统来简化模板的开发.减少代码冗余.
通用视图对通用模式进行抽象,所以你甚至不需要编写一行代码也可以创建一个app.
现在在poll应用中使用通用视图系统,这个就可以把重复的代码去掉了.使用步骤:
1 转换URLconf
2 删除旧的,不必要的视图
3 新的视图使用通用视图
为什么要梳理代码:
通常你在创建一个django app时,会分析通用视图是否能够可以解决你的问题.然后从一开始就使用它,而不是中途重构代码时才引入.本系列教程先是关注如何创建一个app,现在开始注重编程的核心概念.也就是说,你需要有基本的数学基础,才能使用一个计算器.
修改URLconf
编辑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<poll_id>\d+)/vote/$', views.vote, name='vote'), )
修改视图
接下来,移除旧的index,detail,results视图,使用django的通用视图代替.编辑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, Poll class IndexView(generic.ListView): template_name = 'polls/index.html' context_object_name = 'latest_poll_list' def get_queryset(self): """Return the last five published polls.""" return Poll.objects.order_by('-pub_date')[:5] class DetailView(generic.DetailView): model = Poll template_name = 'polls/detail.html' class ResultsView(generic.DetailView): model = Poll template_name = 'polls/results.html' def vote(request, poll_id): ....
上面的代码使用了两个通用视图:ListView和DetailView,这两个视图的含义分别是显示对象列表和显示具体的对象.
每个通用视图都需要知道它将对哪个模型进行操作.
DetailView通用视图期望从URL中获取名为"pk"的主键值,因此我们将poll_id变成pk。
默认情况下,DetailView使用的模板命名规则为<app name>/<model name>_detail.html.它对应这里的模板名为“polls/poll_detail.html"。template_name属性是用于告诉django使用指定的模板名,而不是自动生成的默认模板名。在这里也指定了results list view的模板名——确保它们按照自定义的方式渲染视图。
同样的,ListView通用视图,使用默认的模板名规则<app name>/<model name>_list.html.这里使用了template name属性指定了一个模板名“polls/index.html”
在前面的教程中,模板已经提供了包含poll,latest_poll_list上下文变量的上下文。对于DetailView,poll变量是自动提供的-因为正在使用Poll django 模型,而Django能够为上下文变量定义合适的名称。然而,对于ListView,自动生成的上下文变量叫“poll_list”。不过可以通过给context_object_name属性赋值为你想要使用的名字“latest_poll_list”,另外一种解决方案就是把模板中的latest_poll_list改成poll_list,但是第一种方法更加简单和方便。
运行服务器,使用经过通用视图重构过的poll app。
通用视图的更多细节,阅读:https://docs.djangoproject.com/en/1.6/topics/class-based-views/
来源:https://www.cnblogs.com/chuan-joker/p/django_tutorials_4.html