django 1.8 官方文档翻译: 1-2-4 编写你的第一个Django应用,第4部分

编写你的第一个 Django 程序 第4部分

本教程上接 教程 第3部分 。咱们将 继续开发 Web-poll 应用而且关注在处理简单的窗体和优化咱们的代码。html

编写一个简单的窗体

让咱们把在上一篇教程中编写的 poll 的 detail 模板更新下,在模板中包含 HTML 的 <form> 组件:python

<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>

简单的总结下:数据库

  • 上面的模板中为每一个投票选项设置了一个单选按钮。每一个单选按钮的 value 是投票选项对应的 ID 。每一个单选按钮的 name 都是 “choice”。这意味着,当有人选择了一个单选按钮并提交了表单,将会发送 的 POST 数据是 choice=3。这是 HTML 表单中的基本概念。django

  • 咱们将 form 的 action 设置为 {% url 'polls:vote' poll.id %},以及设置了 method="post" 。使用 method="post" ( 而不是 method="get") 是很是重要的,由于这种提交表单的方式会改变服务器端的数据。 当你建立一个表单为了修改服务器端的数据时,请使用 method="post" 。这不是 Django 特定的技巧;这是优秀的 Web 开发实践。浏览器

  • forloop.counter 表示 for 标签在循环中已经循环过的次数服务器

  • 因为咱们要建立一个POST form ( 具备修改数据的功能 ),咱们须要担忧跨站点请求伪造 ( Cross Site Request Forgeries )。 值得庆幸的是,你没必要太担忧这一点,由于 Django 自带了一个很是容易使用的系统来防护它。 总之,全部的 POST form 针对内部的 URLs 时都应该使用 {% csrf_token %} 模板标签。函数

如今,让咱们来建立一个 Django 视图来处理提交的数据。 记得吗?在 教程 第3部分 中,咱们为 polls 应用建立了一个 URLconf 配置中包含有这一行代码:oop

url(r'^(?P<poll_id>\d+)/vote/$', views.vote, name='vote'),

咱们还建立了一个虚拟实现的 vote() 函数。让咱们建立一个真实版本吧。在 polls/views.py 中添加以下代码:post

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,)))

在这代码中有些内容还未在本教程中提到过:学习

request.POST 是一个相似字典的对象,可让你 经过关键字名称来获取提交的数据。在本例中, request.POST['choice'] 返回了所选择的投票项目的 ID ,以字符串的形式。 request.POST 的值永远是字符串形式的。

请注意 Django 也一样的提供了经过 request.GET 获取 GET 数据的方法 – 可是在代码中咱们明确的使用了 request.POST 方法,以确保数据是经过 POST 方法来修改的。

若是 choice 未在 POST 数据中提供 request.POST['choice'] 将抛出 KeyError 当未给定 choice 对象时上面的代码若检测到抛出的是 KeyError 异常就会向 poll 显示一条错误信息。

在增长了投票选项的统计数后,代码返回一个 HttpResponseRedirect 对象而不是常见的 HttpResponse 对象。 HttpResponseRedirect 对象须要一个参数:用户将被重定向的 URL (请继续看下去在这状况下咱们是如何构造 URL ) 。

就像上面用 Python 做的注释那样,当成功的处理了 POST 数据后你应该老是返回一个 HttpResponseRedirect 对象。 这个技巧不是特定于 Django 的;它是优秀的 Web 开发实践。

在本例中,咱们在 HttpResponseRedirect 的构造方法中使用了 reverse() 函数。 此函数有助于避免在视图中硬编码 URL 的功能。它指定了咱们想要的跳转的视图函数名以及视图函数中 URL 模式相应的可变参数。在本例中,咱们使用了教程 第3部分中的 URLconf 配置, reverse() 将会返回相似以下所示的字符串

'/polls/3/results/'

... 在此 3 就是 p.id 的值。该重定向 URL 会调用 'results' 视图并显示最终页面。

正如在教程 第3部分提到的,request 是一个 HttpRequest 对象。想了解 HttpRequest 对象更多的内容,请参阅 request 和 response 文档 。

当有人投票后,vote() 视图会重定向到投票结果页。让咱们来编写这个视图

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

这几乎和 教程 第3部分 中的 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>

如今,在浏览器中访问 /polls/1/ 并完成投票。每次投票后你将会看到结果页数据都有更新。 若是你没有选择投票选项就提交了,将会看到错误的信息。

使用通用视图:优化代码

detail() ( 在 教程 第3部分 中) 和 results() 视图 都很简单 – 而且还有上面所提到的冗余问题。index() 用于显示 polls 列表的 index() 视图 (也在教程 第3部分中),也是存在相似的问题。

这些视图表明了基本的 Web 开发中一种常见的问题: 根据 URL 中的参数从数据库中获取数据,加载模板并返回渲染后的内容。因为这类现象很 常见,所以 Django 提供了一种快捷方式,被称之为“通用视图”系统。

通用视图抽象了常见的模式,以致于你不须要编写 Python 代码来编写一个应用。

让咱们把 poll 应用修改为使用通用视图系统的应用,这样咱们就能删除删除一些咱们本身的代码了。 咱们将采起如下步骤来进行修改:

  • 修改 URLconf 。

  • 删除一些旧的,没必要要的视图。

  • 修正 URL 处理到对应的新视图。

请继续阅读了解详细的信息。

为何要重构代码?

一般状况下,当你编写一个 Django 应用时,你会评估下通用视图是否适合解决你的问题, 若是适合你就应该从一开始就使用它,而不是进行到一半才重构你的代码。 可是本教程直到如今都故意集中介绍“硬编码”视图,是为了专一于核心概念上。

就像你在使用计算器前须要知道基本的数学知识同样。

修改 URLconf

首先,打开 polls/urls.py 的 URLconf 配置文件并修改为以下所示样子

from django.conf.urls import patterns, 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'),
        name='index'),
    url(r'^(?P<pk>\d+)/$',
        DetailView.as_view(
            model=Poll,
            template_name='polls/detail.html'),
        name='detail'),
    url(r'^(?P<pk>\d+)/results/$',
        DetailView.as_view(
            model=Poll,
            template_name='polls/results.html'),
        name='results'),
    url(r'^(?P<poll_id>\d+)/vote/$', 'polls.views.vote', name='vote'),
)

修改 views

在这咱们将使用两个通用视图: ListView 和 DetailView 。这两个视图分别用于显示两种抽象概念 “显示一系列对象的列表” 和 “显示一个特定类型的对象的详细信息页”。

  • 每一个视图都须要知道使用哪一个模型数据。所以须要提供将要使用的 model 参数。

  • DetailView 通用视图指望从 URL 中捕获名为 "pk" 的主键值,所以咱们将 poll_id 改成 pk 。

默认状况下, DetailView 通用视图使用名为 <应用名>/<模型名>detail.html 的模板。在咱们的例子中,将使用名为 "polls/polldetail.html" 的模板。 templatename 参数是告诉 Django 使用指定的模板名,而不是使用自动生成的默认模板名。 咱们也指定了 results 列表视图的 templatename – 这确保了 results 视图和 detail 视图渲染时会有不一样的外观,虽然它们有一个 DetailView 隐藏在幕后。

一样的,~django.views.generic.list.ListView 通用视图使用的默认模板名为 <应用名>/<模型名>list.html ;咱们指定了 templatename 参数告诉 ListView 使用已经存在的 "polls/index.html" 模板。

在以前的教程中,模板提供的上下文中包含了 poll 和 latestpolllist 上下文变量。在 DetailView 中 poll 变量是自动提供的 – 由于咱们使用了一个 Django 模型 (Poll) ,Django 可以为上下文变量肯定适合的名称。 另外 ListView 自动生成的上下文变量名是 polllist 。若要覆盖此变量咱们须要提供 contextobjectname 选项, 咱们想要使用 latestpoll_list 来替代它。做为一种替代方式,你能够改变你的模板来 匹配新的默认的上下文变量 – 但它是一个很是容易地告诉 Django 使用你想要的变量的方式。

如今你能够在 polls/views.py 中删除 index() , detail() 和 results() 视图了。 咱们不须要它们了 – 它们已替换为通用视图了。你也能够删除再也不须要的 HttpResponse 导入包了。

运行服务器,而且使用下基于通用视图的新投票应用。

有关通用视图的完整详细信息,请参阅 通用视图文档.

当你熟悉了窗体和通用视图后,请阅读 教程 第5部分 来学习测试咱们的投票应用。

译者:Django 文档协做翻译小组,原文:Part 4: Forms and generic views

本文以 CC BY-NC-SA 3.0 协议发布,转载请保留做者署名和文章出处。

Django 文档协做翻译小组人手紧缺,有兴趣的朋友能够加入咱们,彻底公益性质。交流群:467338606。

相关文章
相关标签/搜索