编写你的第一个 Django app,第四部分(Page 9)转载请注明链接地址

该教程上接前面的第三部分。我们会继续开发 web-poll 应用,并专注于简单的表单处理和简化代码。

写一个简单的表单(form)

让我们更新一下我们上个教程编写的的 poll 的 detai 模板(“polls/detail.html”),模板会包含一个 HTML <form> 元素:

<!--polls/templates/polls/detail.html-->
<h1>{{ question.question_text }}</h1> {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} <form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.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>

简要说明:

  • 上面的模板为每个 question 的 choice 显示了一个单选按钮。每个单选按钮的值对应 quesiton 里 choice 的 ID。单选按钮的名字都是 “choice”。这表示,当有人选择其中一个按钮并提交表单,它会发送 POST 数据 choice=#, “#”表示选中的 choice 的 ID。这是 HTML 中 forms 的基本概念。
  • 我们设置 forms 的 action(应该是行为,但不太确定) 为 {% url 'polls:vote' question.id %} ,并设置 method="post"。使用method="post" (与之相反的是method="get")非常重要,因为这个 form 的提交动作会修改服务端的数据。无论何时,你创建的 form 在修改服务端数据时,都要使用 method="post"。这个技巧并不限于 Django;它是一个很好的 web 开发习惯。
  • forloop.counter 表示 for 标签已经循环了多少次。
  • 我们已经创建了一个POST form(它可以修改用来修改数据),我们需要注意伪装的跨站点请求。幸亏,你不需要太过担心,因为django有一个用来防御它的易于使用的系统。简单的说,所有的POST form 有针对性的在内部URLs中使用 {% csrf_token %}(这里少一个链接)模板标签

现在,我们创建一个处理提交的数据的 django 视图,并用它搞一些事情。记住,在前面第三部分的教程中,我们为polls应用创建了一个 URLconf,它包含下面一行:

# polls/urls.py
path('<int:question_id>/vote/', views.vote, name='vote'),

我们还创建了一个 vote() 函数的虚拟实现。现在我们创建一个真正的。在polls/views.py中添加如下内容:

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

这里的代码有少量教程中还没有涉及的内容。

'/polls/3/results/'

3 是 question.id 的值,这个重定向的URL之后会调用 results 视图来显示最终的页面。

正如前一节教程中提到的,request 是一个 HttpRequest(这里少一个链接) 对象。更多关于 HttpRequest(这里少一个链接)的内容,请查看request and response documentation(这里少一个链接)

当有人对一个问题投票后,vote() 视图重定向到问题的结果页面,我们来写一个这个视图:

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

它几乎和前一节中的 detail()视图一样。仅有模板名字不一样。稍后我们会修复这个冗余(说是代码重复准确一些)问题。

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

<!--polls/templates/polls/results.html-->
<h1>{{ question.question_text }}</h1> <ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul> <a href="{% url 'polls:detail' question.id %}">Vote again?</a>

现在,我们在浏览器中打开 “/polls/1/”并给这个问题投票。你应该可以看到每次投票结果更新后的页面。如果你提交的form中没有选中的choice,你会看到一个错误消息。

注意

代码中我们的vote()视图有一个小小的问题,它首先从数据库中得到一个selected_choice 对象,然后计算新的投票结果,并将结果保存到数据库。如果你的站点有两个用户尝试在同一个时间点投票,这可能会导致错误: 相同的值,比如被取回来的投票数是42。然后两个用户都会43这个新值并被保存,而不是预期的值44.

这个叫做竞态条件(race condition:指设备或系统出现不恰当的执行时序,而得到不正确的结果)。如果你感兴趣。你可以阅读 使用F()避免静态条件(这里少一个链接),学习如何解决这个问题。

使用通用视图(Generic views):代码还是少点好

detail() 和 results()视图都很简单 —— 并且,像上面提到的一样,冗余(代码重复)。和index()视图类似,显示投票题目的一个列表。

这些视图反应了web开发中一个常见情况:根据URL中传递的参数从数据库获取数据,记在模板并返回渲染后的模板。由于这个太常见,Django提供了一个快捷的,叫做“通用视图”的系统。

通用视图抽象常见的模式, 可以让你编写app时甚至不需要写python代码。

让我们讲poll app转换成通用视图,这样我们可以删除许多代码。我们仅需要几步来完成转换。我们会:

  1. 调整URLconf
  2. 删除一些旧的,没用的视图
  3. 引入基于Django通用视图的新视图

详情请阅读。

为什么重构代码? (code-shuffle:我理解的是代码重构)

一般,当我们写Django app,你需要评估通用视图是否适合解决你的问题,你可以从一开始就使用它,不是半途重构你的代码。但到现在为止,本教程有意专注使用“困难的方式”编写视图,把重点放在核心概念上。

使用计算器之前,你应该对数学有一个基本的了解。

改进 URLconf

首先,打开 polls/urls.py ,像下面一样修改 URLconf。

# polls/urls.py
from django.urls import path from . import views app_name = 'polls'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]

注意第二个和第三个路径字符串里匹配模式的名字从 <question_id> 变成了 <pk>

改进视图

下一步,我们会移除旧的 indexdetailresults 视图,并用Django的通用视图替换它们。 这需要打开 polls/views.py 文件将它改成类似下面的样子:

# polls/views.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.views import generic from .models import Choice, Question class 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, no changes needed.

这里我们使用了两个通用视图: ListVIew(这里少一个链接)DetailView(这里少一个链接)。这两个视图分别抽象了 “显示一个对象的列表” 和 “显示一个特定类型对象的详情页” 的概念。

  • 每个通用视图需要知道她将作用于那个模型。这由使用的模型的属性提供。
  • DetailView(这里少一个链接) 通用视图期望从URL捕捉到的名为“pk”的主键值,所以我们将question_id 改成 pk 来使通用视图可以找到它。

默认情况下,DetailView(这里少一个链接)通用视图使用一个名为 <app name>/<model name>_detail.html的模板。在我们的例子中,我们使用 "polls/question_detail.html"模板。template_name属性用于告诉Django使用特定的模板名去替换自动生成的默认模板名,我们还需要为 result 列表视图指定 template_name —— 这是为确保result视图和detail视图在渲染时呈现不同的外观,虽然它们后面是同一个 DetailView(这里少一个链接)视图。

类似的,ListVIew(这里少一个链接)通用视图使用了名为 <app name>/<model name>_detail.html 的模板。我们使用 template_name 告诉 ListVIew(这里少一个链接) 去使用我们已经有的 "polls/index.html"模板。

在教程前面的部分,已经提供了一个包含 questionlatest_question_list 的 context 变量的模板。对DetailView来说, question变量已经被自动提供 —— 从我们使用模型(Question)开始,Django可以为context变量取一个合适的名字。然而,对于 ListView,自动生成的context变量是 question_list。重写我们提供的 context_object_name 属性,用我们想使用的 latest_question_list 替代它。作为一种替代方法,你可以更改你的模板来匹配新的默认的context变量 —— 然而直接告诉Django去使用你想用的变量会简单很多。

运行服务器,使用新的基于通用视图的投票app。

更多关于通用视图的内容,请参阅 [通用视图文档](这里少一个链接)()。

当你熟悉了表单和通用视图,可以继续向下学习 测试我们的投票app的内容。

Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第四部分(Page 9)的更多相关文章

  1. Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第一部分(Page 6)

    编写你的第一个 Django app,第一部分(Page 6)转载请注明链接地址 Django 2.0.1 官方文档翻译: Django 2.0.1.dev20171223092829 documen ...

  2. Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第五部分(Page 10)

    编写你的第一个 Django app,第五部分(Page 10)转载请注明链接地址 我们继续建设我们的 Web-poll 应用,本节我们会为它创建一些自动测试. 介绍自动测试 什么是自动测试 测试是简 ...

  3. Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第二部分(Page 7)

    编写你的第一个 Django app,第二部分(Page 7)转载请注明链接地址 本教程上接前面的教程.我们会配置数据,创建你的第一个 model,并对Django 自动生成的 admin 站点进行快 ...

  4. Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第七部分(Page 12)

    编写你的第一个 Django app,第七部分(Page 12)转载请注明链接地址 本节教程承接第六部分(page 11)的教程.我们继续开发 web-poll应用,并专注于自定义django的自动生 ...

  5. Django 2.0.1 官方文档翻译:编写你的第一个 Django app,第六部分(Page 11)

    编写你的第一个 Django app,第六部分(Page 11)转载请注明链接地址 本教程上接前面第五部分的教程.我们构建了一个经过测试的 web-poll应用,现在我们会添加一个样式表和一张图片. ...

  6. Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第三部分(Page 8)

    编写你的第一个 Django app,第三部分(Page 8)转载请注明链接地址 本页教程接前面的第二部分.我们继续开发 web-poll app,我们会专注于创建公共接口上 -- "视图& ...

  7. Django 2.0.1 官方文档翻译:编写你的第一个djang补丁(page 15)

    编写你的第一个djang补丁(page 15) 介绍 有兴趣为社区做一些贡献?可能你发现了django中的一个你想修复的bug,或者你你想添加一个小小的功能. 回馈django就是解决你遇到的问题的最 ...

  8. Django 2.0.1 官方文档翻译: 高级教程:如何编写可重用的app (page 13)

    高级教程:如何编写可重用的app (page 13) 本节教程上接第七部分(Page 12).我们会把我们的 web-poll应用转换成一个独立的python包,你可以在新的项目中重用或者把它分享给其 ...

  9. Django 2.0.1 官方文档翻译: 如何安装 django (Page 17)

    如何安装 django(Page 17) 这一部分可以让你将 Django 运行起来. 安装 Python 作为 python 的一个 web 框架,Django 依赖 Python.Python 的 ...

随机推荐

  1. python learning2.py

    L = ['Michael', 'Sarah', 'Tracy', 'Bob', 'Jack'] # 取前3个元素的笨方法 r = [] n = 3 for i in range(n): r.appe ...

  2. FD.io 社区中国行暨未来网络技术沙龙 南京站 参会小结

    FD.io 社区中国行暨未来网络技术沙龙 南京站,2018 年 3 月 17 日. 开场致辞 Ray 介绍了一些有的没的 ⁃ (Future Event)DPDK summit, FD.io summ ...

  3. AngularJs 学习 (二)

    紧接着第一部分: 推荐阅读: http://adrianmejia.com/blog/2014/10/03/mean-stack-tutorial-mongodb-expressjs-angularj ...

  4. Scala入门系列(三):数组

    Array 与Java的Array类似,也是长度不可变的数组,此外,由于Scala与Java都是运行在JVM中,双方可以互相调用,因此Scala数组的底层实际上是Java数组. 注意:访问数组中元素使 ...

  5. Scrum 项目 3.0

    -------------------------------------3.0----------------------------------------------------- 一.项目工作 ...

  6. 结对编程:四则运算。组员:闫浩楠 杨钰宁 开发语言:C语言

    需求分析:1.能够自动出题并给出答案 2.包含“+,—,*,/,()” 的四则运算. 3.显示题目的答案 结构设计:1.自动出题用随机数生成语句实现:包括随机生成数字.运算符号和题目长度 2.用变量约 ...

  7. 微信小程序 功能函数 获取验证码*

    yanZhengInput: function (e) { var that = this; var yanzheng = e.detail.value; var huozheng = this.da ...

  8. Linux进程调度策略的发展和演变(转)

    转发:http://blog.csdn.net/gatieme/article/details/51701149  1 前言 1.1 进程调度 内存中保存了对每个进程的唯一描述, 并通过若干结构与其他 ...

  9. UpdateBatch到底是怎么用的?

    要使用ADOQuery的UpdateBatch函数,必须将ADOQuery的LockType属性设置成ltBatchOptimistic

  10. 使用 TClientDataSet(1)

    本例效果图: 代码文件: unit Unit1; interface uses   Windows, Messages, SysUtils, Variants, Classes, Graphics, ...