博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[翻译]编写你的首个Django app, part 4
阅读量:6267 次
发布时间:2019-06-22

本文共 6847 字,大约阅读时间需要 22 分钟。

这份指南接着继续讲解。我们将继续进行我们的Web-poll应用,并将注意力放在简单的表单处理及精简我们的代码。

编写一个简单的表单

让我们更新我们前一篇指南中的detail 模板(“polls/detail.html”),以使得那个模板包含一个HTML<form>元素:

polls/templates/polls/detail.html

{
{ question.question_text }}

{% if error_message %}

{

{ error_message }}

{% endif %}
{% csrf_token %}{% for choice in question.choice_set.all %}
{% endfor %}

一个简单的介绍:

  • 上面的模板为每一个question选项显示了一个radio button。每个radio button的值是其关联的question选项的ID。每个radio button的名字是"choice"。那即是说,当有人选择了radio buttons中的一个并提交了表单时,它将发送POST数据choice=#,其中#是选中的选项的ID。这是HTML表单的基本概念。
  • 我们把表单的action设置为{% url 'polls:vote' question.id %},并设置method="post"。使用method="post" (而不是想法使用method="get")是非常重要的,因为提交这个表单的行为将改变服务器端的数据。无论何时你创建了一个表单,其改变了服务器端的数据,请都使用method="post"。这个建议不仅仅适用于Django;它只是一个好的Web开发实践。
  • forloop.counter指示标签执行了多少次循环体。
  • 由于我们在创建一个POST表单(它的执行可能会修改数据),我们需要考虑Cross Site Request Forgeries的问题。谢天谢地,你不需要为此太过担忧,因为Django有一个非常易于使用的系统来做保护。简单的说就是,目标为内部URLs的所有POST表单都应该使用模板标签。

现在让我们创建一个Django view来处理提交的数据,并用它来做点事情。记住,在中,我们为polls应用创建了一个URLconf,其包含这一行:

polls/urls.py
url(r'^(?P
\d+)/vote/$', views.vote, name='vote'),

我们也创建了一个vote()函数的dummy实现。让我们创建一个真实的版本。给polls/views.py添加如下的内容:

polls/views.py
from django.shortcuts import get_object_or_404, renderfrom django.http import HttpResponseRedirect, HttpResponsefrom django.core.urlresolvers import reversefrom polls.models import Choice, Question# ...def vote(request, question_id):    p = get_object_or_404(Question, pk=question_id)    try:        selected_choice = p.choice_set.get(pk=request.POST['choice'])    except (KeyError, Choice.DoesNotExist):        # Redisplay the question voting form.        return render(request, 'polls/detail.html', {            'question': 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,)))

这份代码包含了一些在这份文档中我们之前没有提到过的东西:

  • 是一个类字典对象,它使我们能够通过key名字来访问提交的数据。这个case里,request.POST['choice']以一个字符串的形式返回所选择的choice的ID。值总是字符串

    注意Django也提供了以相同的方式来访问GET数据 —— 但在我们的代码中我们显式地使用,来确保数据只通过一个POST调用改变。

  • 如果在POST数据中没有提供choice,request.POST['choice']将激起。上面的代码检查了,并在没有提供choice时以一条error消息重新显示question表单。

  • 在增加了choice计数之后,代码返回一个而不是一个普通的。接收一个单独的参数:用户将被重定向到的URL(参考下面的说明来了解,我们在这个case中如何构造URL)。

    如同上面的Python注释中指出的,你应该总是在成功处理了POST数据之后返回一个。这个提示不特别用于Django;它只是一个很好的Web开发实践。

  • 这个例子中,我们在构造函数里使用了函数。这个函数帮助避免在view函数中硬编码一个URL。It is given the name of the view that we want to pass control to and the variable portion of the URL pattern that points to that view.在这个case中,使用了我们在Tutorial 3中设置的URLconf,这个调用会返回一个如下这样的字符串:

'/polls/3/results/'
  • ... 其中3是p.id的值。这个重定向的URL将在随后调用'results' view来显示最后的页面。

如同在Tutorial 3中提到的,request是一个对象。要获取更多关于对象的信息,请参考

有人在一个question中投票之后,vote() view会将那个question重定向到results页面。让我们来编写那个view:

polls/views.py

from django.shortcuts import get_object_or_404, renderdef results(request, question_id):    question = get_object_or_404(Question, pk=question_id)    return render(request, 'polls/results.html', {'question': question})

这个函数与中的detail() view几乎完全一样。仅有的不同就是模板的名字。我们将会在后面解决这个冗余。

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

polls/templates/polls/results.html

{
{ question.question_text }}

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

现在在你的浏览器中跳转到/polls/1/,并在question中投票。你应该看到了一个results页面,并且每次在你投票之后它都会得到更新。如果你提交了表单而没有选择一个choice,你应该看到error消息。

使用generic views:代码越少越好

detail() (来自与)results() views都有点太简单了——而且如上面提到的,有些冗余。index() view (也来自于Tutorial 3),它显示一个polls的列表,也类似。

这些views展示了基本Web开发的一个常见场景:根据在URL中传进来的一个参数从数据库获得数据,加载一个模板,并返回rendered的模板。由于这是如此的常见,Django提供了一个快捷方式,称为“generic views”系统。

.Generic views抽象了通用的模式,使得你甚至不需要编写Python code就写出一个app。

让我们转换我们的poll app来使用generic views系统,我们将因此而能够删除大量我们自己的代码。我们只需要做到下面的几步就可以完成转换了。我们将:

  1. 转换URLconf。
  2. 删除一些老的,不需要的views。
  3. 引入新的基于Django的generic views的views。

Read on for details.

Why the code-shuffle?

通常,当你编写一个Django app时,你需要评估对于你要解决的问题而言,generic views是否是一个好方法,然后在一开始就使用它们,而不是做到了一半来重构。但这份指南直到现在一直聚焦于以“the hard way”编写views,来集中于核心概念。

在你开始使用一个计算器之前你应该先了解基本的数据知识。

修改URLconf

首先,打开polls/urls.py 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
\d+)/$', views.DetailView.as_view(), name='detail'), url(r'^(?P
\d+)/results/$', views.ResultsView.as_view(), name='results'), url(r'^(?P
\d+)/vote/$', views.vote, name='vote'),)

注意,第二和第三个模式的正则表达式匹配的模式的name已经由<question_id>变为了<pk>。

修改views

接下来,我们将移除我们老的index,detail和results views,并使用Django的generic views来替换。要做到这一点,打开polls/views.py文件,并像这样来修改它:

polls/views.py

from django.shortcuts import get_object_or_404, renderfrom django.http import HttpResponseRedirectfrom django.core.urlresolvers import reversefrom django.views import genericfrom polls.models import Choice, Questionclass IndexView(generic.ListView):    template_name = 'polls/index.html'    context_object_name = 'latest_question_list'    def get_queryset(self):        """Return the last five published questions."""        return Question.objects.order_by('-pub_date')[:5]class DetailView(generic.DetailView):    model = Question    template_name = 'polls/detail.html'class ResultsView(generic.DetailView):    model = Question    template_name = 'polls/results.html'def vote(request, question_id):    ... # same as above

在这里我们使用了两个generic views:。那两个views分别抽象了“显示一个对象的列表”和“为一个特定类型的对象显示一个详细页面”的概念。

  • 每个generic view需要知道它的行为将作用于的模型(model)。这通过使用model属性来提供。
  •  generic view期望从URL里面称为“pk”的参数中抓取主键值,因而我们为generic views将question_id改为pk。

默认情况下, generic view使用一个称为<app name>/<model name>_detail.html的模板。在我们的例子中,它将使用模板"polls/question_detail.html"。template_name属性被用于告诉Django,要使用一个特定的模板名字来代替自动生成的默认的模板名字。我们也为results list view指定了template_name – 这确保results view和detail view在rendered时有一个不同的外观,尽管在场景背后它们都是一个。

类似地, generic view使用了一个默认的称为<app name>/<model name>_list.html的模板;我们使用template_name告诉来使用我们已有的"polls/index.html"模板。

在这份指南前面的部分,已经通过一个包含有question和latest_question_list context变量的context来提供了模板。对于DetailView,question变量是自动提供地 – 由于我们使用了一个Django model(Question),Django能够为context变量确定一个适当的名称。然而,对于ListView,自动生成的context变量是question_list。要覆写这一点,我们要提供context_object_name属性,指定我们想要用latest_question_list来替换。作为一个可选的方法,你可以修改你的templates来匹配新的默认的context变量 – 但是只告诉Django去使用你想使用的变量要简单得多。

运行服务器,并使用你的基于generic views的新polling app。

关于generic views的完整的细节,请参考

当你熟悉了表单和generic views,就可以阅读,来学习测试我们的polls app的东西了。

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

转载于:https://my.oschina.net/wolfcs/blog/380971

你可能感兴趣的文章
iOS播放短的音效
查看>>
[java] java 线程join方法详解
查看>>
JQuery datepicker 用法
查看>>
golang(2):beego 环境搭建
查看>>
天津政府应急系统之GIS一张图(arcgis api for flex)讲解(十)态势标绘模块
查看>>
程序员社交宝典
查看>>
ABP理论学习之MVC控制器(新增)
查看>>
Netty中的三种Reactor(反应堆)
查看>>
网页内容的html标签补全和过滤的两种方法
查看>>
前端源码安全
查看>>
【CodeForces 618B】Guess the Permutation
查看>>
【转】如何实现一个配置中心
查看>>
Docker —— 用于统一开发和部署的轻量级 Linux 容器【转】
查看>>
Threejs 官网 - Three.js 的图形用户界面工具(GUI Tools with Three.js)
查看>>
Atitit.Java exe bat 作为windows系统服务程序运行
查看>>
session的生命周期
查看>>
数据库的本质、概念及其应用实践(二)
查看>>
iOS开发多线程--(NSOperation/Queue)
查看>>
php的ajax简单实例
查看>>
maven常用构建命令
查看>>