编写你的第一个 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. Java第二天——标识符命名规则、Java的知识、快捷键的使用、Scanner获取值的常用方法

    1.标识符命名规则 字母.下划线.数字.美元符号($)由这四个部分组成. 标识符=首字母+其他 首字母:字母.下划线.美元符号($) 其他:字母.下划线.数字.美元符号($) 注意: 1.首字母不能为 ...

  2. mvc学习-编辑提交需要注意-mvc重点

    示例代码: // GET: /Movies/Edit/5 public ActionResult Edit(int? id) { if (id == null) { return new HttpSt ...

  3. Internet History, Technology and Security (Week5.1)

    Week5 The Transport layer is built on the Internetwork layer and is what makes our network connectio ...

  4. beta阶段博客合集

    第一次博客 第二次博客 第三次博客 第四次博客 第五次博客

  5. TiDB注意事项

    公司最近在上测试的TiDB集群,这款数据库类似MySQL,但又不完全一致,在使用的时候有一下注意事项,在这里记录一下.

  6. pycharm安装jpype报错及解决方法

    安装jpype时发生报错: 按照提示去装了Microsoft visual C++,结果重新安装还是报错,根据https://blog.csdn.net/qq_38934189/article/det ...

  7. [转帖]CPU 的缓存

    缓存这个词想必大家都听过,其实缓存的意义很广泛:电脑整机最大的缓存可以体现为内存条.显卡上的显存就是显卡芯片所需要用到的缓存.硬盘上也有相对应的缓存.CPU有着最快的缓存(L1.L2.L3缓存等),缓 ...

  8. [转帖]TLS 版本问题

    转帖 From https://www.cnblogs.com/xjnotxj/p/7252043.html 一.环境: CentOS 6.8nginx 1.6.0php 7.0.10 二.背景 最近 ...

  9. 【CSS】规范大纲

    文件规范: 文件分类 : 通用类 :业务类. 文件引入:行内样式(不推荐):外联引入:内联引入.(避免使用Import引入) 文件本身:文件名. 编码:UTF-8. 注释规范: 块状注释:统一缩进,在 ...

  10. html实现鼠标悬停变成手型实现方式

    1.采用a标签实现的方式 <a href="javascript:void()">内容</a> 2.采用CSS实现的方式 // 变手形 oElement.s ...