Django1.5:(二)创建第一个Django应用4

来源:互联网 发布:剑三丐帮成女捏脸数据 编辑:程序博客网 时间:2024/06/05 02:55

      继续根据Django官网文档实践Django 1.5.1 + Python 3.3写成的Poll应用。本节关注form过程和通用视图。

4.1 写一个form

     细化detail模板,在polls/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.count }}" value="{{ choice.id }}" />    <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />{% endfor %}<input type="submit" value="Vote" /></form>

这一段代码有几点需要解释一下:1)模板为每个poll choice显示了一个radio按钮,每个按钮的value和choice的ID关联,name都是"choice"。这就意味着有人选择了按钮并且提交了,
它就会发送POST数据,例如choice=3。这是HTML forms的基本概念;2)我们设置form的action为{% url ’polls:vote’ poll.id %},method="post"用于改变服务器端的数据;3)forloop.counter给出for标签循环多少次;4)我们创建了一个POST表单,我们就需要担心跨站点请求伪造(CSRF攻击)的问题,Django提供了一个简单易用的保护机制,只需要添加{% csrf_token %}模板标签就可以了。

       接下来我们创建一个Django视图来处理提交的数据,还记得我们在polls/urls.py中创建的url(r’^(?P<poll_id>\d+)/vote/$’, views.vote, name=’vote’),让我们写一个真实的版本,在views.py中修改成这样的代码:

from django.shortcuts import render, get_object_or_404from django.http import HttpResponse, HttpResponseRedictfrom django.core.urlresolvers import reversefrom polls.models import Choice, Polldef 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:resuls', args=(p.id,)))    

       有几点注释:1)request.POST是像字典一样的对象,可以通过key名获取提交的数据,在这里request.POST[’choice’]返回choice的ID,作为一个字符串,其value总是字符串类型的。2)如果choice没有用在POST中提供,request.POST[’choice’]会引发一个KeyError,如果choice没有选择会显示错误星系,重新回到表单页面。3)在计算了choice后,返回一个HttpResponseRedirect而不是通常的HttpResponse。HttpResponseRedirect只有一个参数也就是需要重新定向页面的URL。注意,重定向是惯例。4)reverse()函数可以避免加入植入式的URL,只给出views和变量,这里会返回类似'polls/3/results/'这样的字符串。3是p.id,results是调用的视图函数。

       既然提到了results页面,就让我们补充完整这个视图:

def results(request, poll_id):    poll = get_object_or_404(Poll, pk=poll_id)    return render(request, 'polls/results.html', {'poll': poll})

       它几乎和detail()是一样的,只是模板名字不同而已,我们在后面的通用视图部分修改这样的冗余。polls/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>

4.2 通用视图:更少的代码更好

      detail()视图和results()视图非常简单,而且几乎一样,index()视图展示polls列表,也很相似。

      这些视图代表基本的Web开发的情形,根据URL中的参数从数据库中取出数据,下载模板,渲染模板。由于这些都是常规操作,Django提供了快捷方式,称为通用视图。通用视图将通常的模式抽象化,这样我们可以删去许多冗余代码,我们分为几部来完成这个转变:1)转换URLconf;2)删去一些不需要的视图;3)以通用视图为基础引入新的视图。一般情况下载写app前就应该想是否适合用通用视图,而不是重写代码,这里只是演示的需要。

修改URLconf

       首先打开polls/urls.py修改成这个样子:

from django.conf.urls import patterns, urlfrom polls import viewsurlpatterns = 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'),    )

修改views

       删除index、detail和results视图,采用Django通用视图。在polls/views.py中像这样:

from django.shortcuts import get_object_or_404, renderfrom django.http import HttpResponse, HttpResponseRedirectfrom django.core.urlresolvers import reversefrom django.views import genericfrom polls.models import Choice, Pollclass IndexView(generic.ListView):    template_name = 'polls/index.html'    context_object_name = 'latest_poll_list'        def get_queryset(self):        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,所以我们在URLconf中做了相应修改。默认情形下,DetailView通用视图采用一个<app name>/<module name>_detail.html的模板,我们这里在模板属性中具体指明了。
       在前面模板需要一个包括了poll和latest_poll_list的context。对于DetailView,poll变量自动产生,因为我们使用了Poll模型,Django会合理选择context变量。对于ListView,自动产生的context变量是poll_list,和我们想要的冲突,所以我们要覆盖它,用context_object_name属性指明我们要使用latest_poll_list。当然你也可以在模板中迁就通用视图自动产生的变量名,但是语言应该迁就用户!我们指定它吧。

       表单和通用视图的内容到此借此,更多详细地介绍参加官方文档相应部分。

       下一节我们介绍Poll应用的自动测试。     

原创粉丝点击