编写第一个Django程序(4)

编写第一个Django程序(3)
ok,我们接着之前的第3篇,开始我们的最后一篇.

创建一个简单的表单

我们修改一个测试页面的模板(polls/detail.html).

{{ poll.question }}

{% if errormessage %}

{{ errormessage }}

{% endif %}
{% csrftoken %} {% for choice in poll.choiceset.all %}
{% endfor %}

简单说明一下:

1.上面的模板用单选框来展示一个民意测试的所有投票项.它的值就是对应的ID,他的名称就是对应选项的名称,他的作用就是当选中一个选项,然后提交表单,他将会传送一个post数据,choice=3

2. 我们把这个表单的action写作’ /polls/{{ poll.id }}/vote/‘,方法写为post.

3. forloop.counter指明循环多少次.

4. 既然我们创建了一个提交数据的表单,我们就需要关心注意 一下Cross site request forgeries.不错的是,你不需要担心怎么实现.Django已经内建通过支持了.简单的说,就是所有的post表单都指向到内部的一个地址,我们使用{% csrf_token%}标签完成.

这个{% csrf_token %}标签要从请求对象中获取信息,在模板的内容中我们不能获取它.我们需要在detail的view方法中做一个小的改动:

from django.template import RequestContext # ... def detail(request, pollid): p = getobjector404(Poll, pk=pollid) return rendertoresponse('polls/detail.html', {'poll': p}, contextinstance=RequestContext(request))

详细的说明请参考:https://docs.djangoproject.com/en/1.3/ref/templates/api/#subclassing-context-requestcontext

现在,我们创建view方法来处理提交过来的请求数据,在第三节中我们有这样一句:

(r'^(?P\d+)/vote/$', 'vote'),

下面来修改vote函数来干点实事:

from django.shortcuts import getobjector404, rendertoresponse from django.http import HttpResponseRedirect, HttpResponse from django.core.urlresolvers import reverse from django.template import RequestContext from polls.models import Choice, Poll # ... def vote(request, pollid): p = getobjector404(Poll, pk=pollid) try: selectedchoice = p.choiceset.get(pk=request.POST['choice']) except (KeyError, Choice.DoesNotExist): # Redisplay the poll voting form. return rendertoresponse('polls/detail.html', { 'poll': p, 'errormessage': "You didn't select a choice.", }, contextinstance=RequestContext(request)) else: selectedchoice.votes += 1 selectedchoice.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.views.results', args=(p.id,)))

在这里做点说明:

1,request.POST相当于是一个字典的对象来让我们访问请求传递过来的数据通过key.在这个时候我们就能够通过request.POST[‘choice’]来获取选中项的ID,他的类型始终会是string.

同时,Django也提供了reques.GET来获取get方式传递过来的值,这里我们显式调用POST方法来确保获取的值是通过post方式过来的.

2,request.POST[‘choice’]将会在没有取到对应的值的时候报错.上面的代码检查了这个KeyError,然后会在没有选中选项的情况下返回页面显示错误信息.

3,在增加了投票数之后会重定向到结果页面.我们使用了HttpResponseRedirect来代替HttpResponse.HttpResponseRedirect有一个参数,就是将要重定向的地址.

为什么我们使用重定向呢?不仅仅是python要求这么做,而是作为web开发都是必须的.

4,我们使用了reverse()函数在HttpResponseRedirect参数里,这个函数能够帮助避免硬编码地址在视图的函数中.我们给的视图的名称我们希望传递到对应的控制器,url模式里的变量部分指向的就是视图,这个时候我们的例子中的urlconf中的配置,我们 会把它reverse到/polls/3/results/

关于更多的HttpRequest请参考:https://docs.djangoproject.com/en/1.3/ref/request-response/

在投票结束后会被重定向到results视图,也就是会到views里的results函数.编写它:

def results(request, pollid): p = getobjector404(Poll, pk=pollid) return renderto_response('polls/results.html', {'poll': p})

这个和的detail的差不多.唯一不同的就是返回给的模板不一样.以后会减少这个重复.

现在编写results.html模板:

{{ poll.question }}

    {% for choice in poll.choice_set.all %}
  • {{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}
  • {% endfor %}
Vote again?

到这里基本上就算是结束了整个一个过程.你可以去投票看看效果了!

不过我们还是要有点修改:

使用一些基本的视图来让代码变得更加简洁.

现在我们的detail页面和results页面都是傻到丑的地步,还有就是有点重复,首页也是展现了一个调查的列表.

这些都是一种基本的web开发的情形:通过链接传过来的参数然后从数据库去取数据,然后加载一个模板然后把取得的数据展现在模板上返回显示.因为这个实在是太常见了.所以Django内建了一个叫做’generic views’系统.

Generic views(我这里称作是共通视图)能够把视图中那些共通的部分给抽出来,然后就不需要再去编码了.让我们来把现有的程序添加上共通视图吧.只需一小步哦!

1.重写URLconf

2.删除旧的不用的views

3.给新的views添加URL处理

首先.打开polls/urls.py,修改成下面的这样:

from django.conf.urls.defaults import * from django.views.generic import DetailView, ListView from polls.models import Poll urlpatterns = patterns('', url(r'^$', ListView.asview( queryset=Poll.objects.orderby('-pubdate')[:5], contextobjectname='latestpolllist', templatename='polls/index.html')), url(r'^(?P\d+)/$', DetailView.asview( model=Poll, templatename='polls/detail.html')), url(r'^(?P\d+)/results/$', DetailView.asview( model=Poll, templatename='polls/results.html'), name='poll_results'), url(r'^(?P\d+)/vote/$', 'polls.views.vote'), )

我们用到了2个共通的视图.ListView和DetailView.ListView视图就是显示一个对象list.DetailView就是显示一个对象的详细信息.
1.每一个共通视图都需要知道显示什么对象.所以需要指明.通过model参数.
2.详细的共通视图需要有一个主键所以我们指定pollid为pk.
3.我们添加了一个’poll
results’的结果视图到了vote()里的重定向目标.

默认的,详细视图需要一个模板称作/detail.html.在我们的例子中,我们的模板名称是’polls/polldetail.html’,这个文件名是用来告诉django我在使用特定的模板名来代替自动生成的默认模板文件名.
我们同样需要给results视图指定特定的文件名.这样做是为了确保这两个视图拥有不一样的外观,即使他们使用的都是详细视图.

同样,列表视图也是用一个默认的命名/_list.html.我们通过文件名来告诉列表视图来使用polls/index.html模板.

在之前的章节中我们提到的模板都是包含poll或者latestpolllsit等变量的.在详细的视图中poll对象是被自动提供的.-即我们程序中的对象Poll.Django能够确定当前视图中当前上下文中用到的具体的对象.可是在list视图中,自动生成的变量名就是polllist了.我们能够通过contextobjectname选项来重写这个变量,也就你想用的latestpoll_list变量了.

现在你就能够删掉index(),detail()和results()函数了.我们不在需要他们.他们的功能已经在共通视图中 搞定了.最后一件事情就是搞定URL映射.在上面的投票视图中,我们需要到reverse()函数来避免硬编码.现在我们转到了共通视图.我们需要修改reverse()方法来指向到一个新的视图.把上面的vote()方法中的reverse()中的参数改一下:

return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))

至此,全部已经结束,运行程序,体验一下吧~
有关于更多的关于Generic views的知识,请访问:https://docs.djangoproject.com/en/1.3/topics/http/generic-views/