【译】自己动手写Django app,第四部分【全剧终】

原文地址:https://docs.djangoproject.com/en/1.4/intro/tutorial04/

这个教程是从教程3剩下的地方开始的。我们继续网络调查应用程序并将关注简单的表单处理和精简我们的代码。

一、写一个简单的表单

让我们从上个教程中更新我们的调查详细模板(“polls/detail.html”),这样模板就包含一个HTML<form>元素:

<h1>{{ poll.question }}</h1>

{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

<form action="/polls/{{ poll.id }}/vote/" 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 }}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>
快速摘要:

    上面的模板在每个调查选择上显示一个单选按钮。每个单选按钮的值和调查选择的ID相关联。每个单选按钮的名字就是“choice”。这就是说,当有人选择一个单选按钮然后提交表单,你将发送POST数据choice=3。这就是HTML表单101;

    我们把表单的动作指向/polls/{{ poll.id }}/vote/,我们设置method="post"。用method="post"(相反的是method="get")很重要,因为提交这个表单的动作会改变数据的服务器端。不管什么时候你创建了一个改变数据服务器端的表单,使用method="post"。这个小知识不仅仅对Django有用。这是好的网络开发经验;

    forloop.counter暗示for标签在它的循环中经历了多少次;

    既然我们创建了一个POST表单(它有修改数据的影响),我们需要担心跨站点请求伪造。但是高兴的是,你不需要担心太多,因为Django提供了一个很容易使用的系统来抵御它。简而言之,内部URLs针对的所有POST表单应该用{% csrf_token %}模板标签;

{% csrf_token %}标签需要来自请求对象的信息,它通常从模板环境不可接触。为了修复这个,我们需要对detail视图做小的调整,让它看起来像下面一样:

from django.template import RequestContext
# ...
def detail(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('polls/detail.html', {'poll': p},
                               context_instance=RequestContext(request))
它如何工作的细节在RequestContext文档中已经解释了。

现在,让我们创建一个处理提交数据的Django视图并对它做一些事情。记住,在教程3中,我们为调查应用程序创建一个URLconf包含了下面的一行:

(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
我们也创建了一个假的实现vote()函数。让我们创建一个真的版本,在polls/views.py中增加下面:
from django.shortcuts import get_object_or_404, render_to_response
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, 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_to_response('polls/detail.html', {
            'poll': p,
            'error_message': "You didn't select a choice.",
        }, context_instance=RequestContext(request))
    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.views.results', args=(p.id,)))
这个代码包含了一些我们在这个教程中还没有设计到的东西:

    request.POST是一个像字典的对象让你可以按照键名访问提交的数据。这种情况下,request.POST['choice']返回选中的选择的ID作为一个字符串。request.POST的值总是一个字符串。注意Django总是用相同的的方式提供request.GET访问GET的数据——但是我们在我们的代码中明确使用request.POST来确保数据通过POST调用改变了。

    如果POST数据没有提供choice,request.POST['choice']会产生KeyError。上面的代码检查KeyError,如果choice没有,就会重新显示一个带有错误信息的调查表单;

    在这个例子中,我们在HttpResponseRedirect容器中用reverse()函数。这个函数帮助避免在视图函数中处理硬编码。这是考虑到的视图名称,我们想通过控制和可变部分的URL模式来指出这个视图。在这种情况下,用我么你在教程3中创建的URLconf,这个reverse()函数将会返回一个像下面的字符串:

'/polls/3/results/'
——这里的3是p.id的值。这个重定向URL会调用'results'视图来显示最终的页面。注意这儿你需要用视图的全名(包括前缀)。

在教程3中已经提到了,request是一个HttpResponse对象。更多关于HttpResponse对象,请看请求和响应文档。

当有人投票了一个调查之后,vote()视图重定向调查结果页面。让我们写这个视图:

def results(request, poll_id):
    p = get_object_or_404(Poll, pk=poll_id)
    return render_to_response('polls/results.html', {'poll': p})
这基本和教程3中的detail()视图是完全一样。唯一的区别就是名字。我们之后会修复这个冗余。

现在,创建一个results.html模板:

<h1>{{ poll.question }}</h1>

<ul>
{% for choice in poll.choice_set.all %}
    <li>{{ choice.choice }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>

<a href="/polls/{{ poll.id }}/">Vote again?</a>
现在,在你的浏览器中查看/polls/1/然后再调查程序中投票。你应该看到一个更新每次你投票的结果页面。如果你没有做出选择就提交了表单,你应该能看到错误信息。

二、使用通用视图:代码越少越好

detail()(来自教程3)和results()视图很蠢很简单——就像上面提到的一样,冗余。index()(来自教程3)视图类似显示一系列调查。

这些视图表示基本网络开发的常例:根据URL传递的参数从数据库获得数据,载入模板,然后返回提供的模板。因为这是共同的,Django提供一个快捷方式叫做“通用视图”系统。

通用视图把一般的模式抽象到你不需要写python代码的程度。

让我们用通用视图系统转换我们的调查程序,这样我们就能删掉许多我们的代码。我们只要采取一些步骤来实现这个转换。我们会:

    1.转换URLconf;

    2.删除一些老的不需要的视图;

    3.修复处理新的视图的URL;

看看详情。

(为什么搅乱代码?:通常来讲,当写一个Django应用程序时,你需要估计通用视图是否适用于你的问题,你将在开始用它们而不是半途重构你的代码。但是这个教程直到现在的目的关注“硬方式”写视图,关注核心理念。在你开始用计算器之前你应该了解基本的数学。)

首先,打开poll/urls.py的URLconf。根据到目前的教程,它看起来像这样:

from django.conf.urls import patterns, include, url

urlpatterns = patterns('polls.views',
    url(r'^$', 'index'),
    url(r'^(?P<poll_id>\d+)/$', 'detail'),
    url(r'^(?P<poll_id>\d+)/results/$', 'results'),
    url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
)
把它改成下面这样:
from django.conf.urls import patterns, include, url
from django.views.generic import DetailView, ListView
from polls.models import Poll

urlpatterns = patterns('',
    url(r'^$',
        ListView.as_view(
            queryset=Poll.objects.order_by('-pub_date')[:5],
            context_object_name='latest_poll_list',
            template_name='polls/index.html')),
    url(r'^(?P<pk>\d+)/$',
        DetailView.as_view(
            model=Poll,
            template_name='polls/detail.html')),
    url(r'^(?P<pk>\d+)/results/$',
        DetailView.as_view(
            model=Poll,
            template_name='polls/results.html'),
        name='poll_results'),
    url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote'),
)
在这里我们使用了两个通用视图:ListView和DetailView。分别地,这两个视图抽象“显示一些列对象”和“显示一个对象类型的详细页面”的概念。

    每个通用视图需要知道它作用的模型。这是用模型参数提供的;

    DetailView通用视图期待从URL中捕获的第一个关键字的值(被称作“px”),因此我们为了通用视图把poll_id改成px;

    我们已经给结果视图增加了一个名字poll_results,这样我们之后有一种提到它的URL方式(详细信息请查看命名URL模式文档)。这里我们也从django.conf.urls调用url()函数。当你提供一个像这样模式的名字的时候,使用url()是个好习惯。

默认情况下,DetailView通用视图用一个叫做<app name>/<model name>_detail.html模板。在我们的例子中,它用一个模板“polls/poll_detail.html”。template_name参数是用来告诉Django一个明确的模板名字而不是一个自动生成的默认模板名字。我们也规定resultstemplate_name的列表视图——这确保结果视图和细节视图在显示的时候有不同的外观,即使他们幕后都是DetailView。

类似地,ListView通用视图用一个叫做<app name>/<model name>_list.html的默认模板,我们用template_name告诉ListView用我们存在的polls/index.html模板。

在教程的前面部分,已经提供了带有上下文的模板,它包含了poll和latest_poll_list环境变量。对DetailView来说,poll变量是自动提供的——因为我们用一个Django模型(Poll),Django能决定环境变量的合适名字。然而,对于ListView来说,自动生成的上下文变量是poll_list。覆写这个,我们提供了context_object_name选项来明确我们想用latest_poll_list。作为一个可选的方式,你可以改变你的模板来匹配新的默认的环境变量——但是告诉Django用你想用的变量更容易。

你现在可以从polls/views.py中删除index(),detail()和results()。我们不再需要他们了——他们已经被通用视图代替了。

最后要做的是解决通用视图的使用时URL的处理。在上面的投票视图中,我们用reverse()函数避免我们的URLs硬编码。现在我们已经切换到通用视图,我们需要改变reverse()调用指向我们新的通用视图。我们不能简单地使用视图函数——通用视图可以用许多次——但是我们可以用我们给定的名字:

return HttpResponseRedirect(reverse('poll_results', args=(p.id,)))
运行开发服务器,用你基于通用视图的调查应用程序。

更多关于通用视图的视觉,请查看通用视图文档。

三、即将来临

这个教程眼下在这结束了。教程更多的安装将会涉及:

    表单高级处理;

    用RSS框架;

    用缓存框架;

    用评价框架;

    高级管理特征:权限;

    高级管理特征:定制JavaScript;

同时,你可能想查看一些点,从这里到哪里去。

教程4结束!

整个教程结束,明天开始Django-cms的官方文档!

转载于:https://my.oschina.net/davidxp/blog/110506

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值