Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第四部分(Page 9)
编写你的第一个 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,)))
这里的代码有少量教程中还没有涉及的内容。
request.POST
(这里少一个链接)是一个类似字典的对象,让你可以通过key的名字访问提交的数据。在这里,request.POST['choice']
以字符串的形式返回选中的choice的ID。request.POST
(这里少一个链接)的值总是字符串。
注意,django也以同样的方式提供用来访问GET数据的request.GET
(这里少一个链接) —— 但是为确保数据只可以通过POST调用修改数据,我们要在我们的代码中明确使用request.POST
(这里少一个链接)。- 如果POST数据中没有
choice
,request.POST['choice']
会引发一个KeyError
(这里少一个链接),上面的代码对么有给定choice
时赢啊的KeyError
这里少一个链接)做了检查,此时会重新显示quesiton form。- 当choice的票数增加后,代码返回了一个
HttpResponseRedirect
(这里少一个链接) 而不是一个常用的HttpResponse
(这里少一个链接) 。HttpResponseRedirect
(这里少一个链接) 只接收一个参数:用户将被重定向后的URL(下面的内容会介绍如何构建一个本例中的URL)。
正如上面python注释指出的一样,当你成功处理了POST数据之后,也应该返回一个HttpResponseRedirect
(这里少一个链接) 。这个技巧并不限于 Django;它是一个很好的 web 开发习惯。- 在这个例子中,我们在
HttpResponseRedirect
(这里少一个链接) 构造器中使用reverse()
(这里少一个链接)函数。这个函数可以有助于避免在视图函数中硬编码URL。它需要我们给出我们想要跳转的视图的名字和指向该视图的URL模式中的一部分变量,在本例中,我们使用前一节教程中设置的URLconf,reverse()
(这里少一个链接)会返回一个类似下面的字符串:'/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转换成通用视图,这样我们可以删除许多代码。我们仅需要几步来完成转换。我们会:
- 调整URLconf
- 删除一些旧的,没用的视图
- 引入基于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>
。
改进视图
下一步,我们会移除旧的 index
、detail
和 results
视图,并用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"
模板。
在教程前面的部分,已经提供了一个包含 question
和 latest_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)的更多相关文章
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第一部分(Page 6)
编写你的第一个 Django app,第一部分(Page 6)转载请注明链接地址 Django 2.0.1 官方文档翻译: Django 2.0.1.dev20171223092829 documen ...
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第五部分(Page 10)
编写你的第一个 Django app,第五部分(Page 10)转载请注明链接地址 我们继续建设我们的 Web-poll 应用,本节我们会为它创建一些自动测试. 介绍自动测试 什么是自动测试 测试是简 ...
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第二部分(Page 7)
编写你的第一个 Django app,第二部分(Page 7)转载请注明链接地址 本教程上接前面的教程.我们会配置数据,创建你的第一个 model,并对Django 自动生成的 admin 站点进行快 ...
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第七部分(Page 12)
编写你的第一个 Django app,第七部分(Page 12)转载请注明链接地址 本节教程承接第六部分(page 11)的教程.我们继续开发 web-poll应用,并专注于自定义django的自动生 ...
- Django 2.0.1 官方文档翻译:编写你的第一个 Django app,第六部分(Page 11)
编写你的第一个 Django app,第六部分(Page 11)转载请注明链接地址 本教程上接前面第五部分的教程.我们构建了一个经过测试的 web-poll应用,现在我们会添加一个样式表和一张图片. ...
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第三部分(Page 8)
编写你的第一个 Django app,第三部分(Page 8)转载请注明链接地址 本页教程接前面的第二部分.我们继续开发 web-poll app,我们会专注于创建公共接口上 -- "视图& ...
- Django 2.0.1 官方文档翻译:编写你的第一个djang补丁(page 15)
编写你的第一个djang补丁(page 15) 介绍 有兴趣为社区做一些贡献?可能你发现了django中的一个你想修复的bug,或者你你想添加一个小小的功能. 回馈django就是解决你遇到的问题的最 ...
- Django 2.0.1 官方文档翻译: 高级教程:如何编写可重用的app (page 13)
高级教程:如何编写可重用的app (page 13) 本节教程上接第七部分(Page 12).我们会把我们的 web-poll应用转换成一个独立的python包,你可以在新的项目中重用或者把它分享给其 ...
- Django 2.0.1 官方文档翻译: 如何安装 django (Page 17)
如何安装 django(Page 17) 这一部分可以让你将 Django 运行起来. 安装 Python 作为 python 的一个 web 框架,Django 依赖 Python.Python 的 ...
随机推荐
- C1WPF制作OLAP Cube浏览工具
经过前期一段时间对WPF的学习了解,相信大家对WPF有了一定的了解.今天我们一起来了解使用Component One(简称C1)的WPF控件制作CUBE浏览工具.其实这个OLAP控件官方已经有了很详细 ...
- 全选练习-原生版和jQuery
今天来做一些练习,做全选练习 原生版的实现: <!DOCTYPE html> <html> <head> <meta charset="UTF-8& ...
- 关于SVM数学细节逻辑的个人理解(一) :得到最大间隔分类器的基本形式
网上,书上有很多的关于SVM的资料,但是我觉得一些细节的地方并没有讲的太清楚,下面是我对SVM的整个数学原理的推导过程,其中逻辑的推导力求每一步都是有理有据.现在整理出来和大家讨论分享. 因为目前我的 ...
- Ubuntu下面 PHPSTORM2017.2破解方法
Ubuntu下面 PHPSTORM2017.2破解方法 下载破解文件 在 http://idea.lanyus.com/上面新下载一个破解文件. 破解步骤 将JetbrainsCrack-2.6.3_ ...
- SQLSERVER 升级版本的方法
1. 以SQLSERVER2014为例说明 SQLSERVER升级版本的方法, 也适用于evaluation 版本超过180天之后的处理. 2. 打开所有的应用 看到有一个 sqlserver2008 ...
- Nginx负载均衡配置与负载策略
原理 负载均衡的目的是为了解决单个节点压力过大,造成Web服务响应过慢,严重的情况下导致服务瘫痪,无法正常提供服务. 应用场景 春节期间在12306网站上买过火车票的朋友应该深有体会,有时查询一张火车 ...
- input accept 属性
*.3gpp audio/3gpp, video/3gpp 3GPP Audio/Video *.ac3 audio/ac3 AC3 Audio *.asf allpication/vnd.ms-as ...
- Handler,Looper,HandlerThread浅析
Handler想必在大家写Android代码过程中已经运用得炉火纯青,特别是在做阻塞操作线程到UI线程的更新上.Handler用得恰当,能防止很多多线程异常. 而Looper大家也肯定有接触过,只不过 ...
- eclipse中添加配置文件夹config
1. 在项目上右键->Build path->Configure Build Path->Source下的Add Folder,如图 2. 在弹出框中,Create New Fold ...
- 【刷题】BZOJ 3531 [Sdoi2014]旅行
Description S国有N个城市,编号从1到N.城市间用N-1条双向道路连接,满足 从一个城市出发可以到达其它所有城市.每个城市信仰不同的宗教,如飞天面条神教.隐形独角兽教.绝地教都是常见的信仰 ...