Django 2.0 学习(07):Django 视图(进阶-续)
接Django 2.0 学习(06):Django 视图(进阶),我们将聚焦在使用简单的表单进行处理和精简代码。
编写简单表单
我们将用下面的代码,来替换之前的detail模板("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>
快速理解:
- 上述模板为每个问题选项添加了一个单选按钮,该按钮的值与问题选项的ID关联,每个按钮的名字都是"choice".这就意味着,当有人选择某个单选按钮并且提交的时候,选择的单选按钮会发送POST数据choice=#,其中#就是选择的choice;
- 设置表单的action为{% url 'polls:vote' question.id %},设置method="post";这部分内容已经超出Django的范畴,属于Web开发的范畴;
- 在使用POST表单时,需要考虑到跨站点请求伪造(Cross Site Request Forgeries).幸运的是,我们不需要考虑的太多,因为Django已经帮我们处理的很好了。简言之,我们所需要做的是:在POST表单请求里面添加{% csrf_token %}模板标签。
接下来,创建视图函数来对提交的数据进行处理。编辑polls/views.py,其代码如下:
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):
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice."
})
else:
selected_choice.votes += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
上述代码涉及部分内容做如下解释:
- request.POST是个像字典的对象,可以通过该对象的关键字(key)来获取提交数据。在这里,request.POST['choice']返回被选择选项的ID,request.POST返回值总是字符串;
- 如果在POST数据中没有提供choice,request.POST['choice']将会抛出KeyError异常。如果未提供choice,上述代码会检查KeyError并且通过一个错误信息来重新显示该问题;
- 在增加选择计数后,代码返回HttpResponseRedirct而不是HttpResponse.HttpResponseRedirect携带一个单独的参数:用户被重定向到哪里的URL;作为优秀的Web开发,POST请求成功应该总是返回HttpResponseRedirect,这并不是Django特别要求的;
- 在HttpResponseRedirect构造其中,我们使用了reverse()方法。该方法避免在视图函数中使用URL硬编码,他会提供我们想传递的视图函数的名字并且在ULR模式中找到该视图。
在有人对某个问题投票后,vote()视图会把结果重定向到结果页面,下面代码是完善的视图函数:
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question': question})
现在创建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>
request就是个HttpRequest对象,更多信息详见HttpRequest
现在,在浏览器中访问/polls/1/并且对问题进行投票。我们会看到如下页面:
如果没有选择任何选项,会看到错误信息,如下图所示:
注:上面的vote()视图有个小小的问题。该函数第一次从数据库中获取selected_choice对象,接下来计算新的votes的值,并且将其保存回数据库中。同一时刻,如果有两个用户都发起投票,就会出现下面的错误:将会得到相同的votes值,假设现在的值是42,然而对两个用户来说43这个新的值将会被计算和保存,但是我们期望的值是44。
通用视图:更少的代码更好
上述视图函数代表了基本Wbe开发的一种通用情况:根据URL传递的参数从数据库中获取数据,加载模板并返回渲染后的模板。因为这是公共的,所以Django提供了一种快捷方式,称之为"通用视图"系统。
让我们使用通用视图系统来改造之前的代码,其步骤如下:
1、改变URLconf;
2、删除旧的、非必要的视图函数;
3、基于Django的通用视图,引入新的视图;
首先,修改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
其次,修改视图函数(使用Django的通用视图系统替换旧的index,detail和results视图):打开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 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):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice."
})
else:
selected_choice.votes += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
# return HttpResponse("You're voting on question %s." % question_id)
我们这里使用两个通用视图:ListView和DetailView,这两个视图分别代表了"显示对象的列表"和"显示某个对象的详细信息"。
- 使用模型属性,每个通用视图需要知道它所作用的模型;
- DetailView通用视图期望主键值能通过URL中获取到,所以在通用视图中我们把question_id改成pk;
默认情况,DetailView通用视图使用一个叫做app name/model name_detail.html的模板。在我们的示例中,将会使用模板"polls/question_detail.html"。template_name属性用来告诉Django使用一个具体的名字代替自动生成的默认的模板名字。
同样的,ListView通用视图使用默认模板app name/model name_list.html;我们使用template_name告诉ListView使用我们的polls/index.html模板。
Django 2.0 学习(07):Django 视图(进阶-续)的更多相关文章
- Django 2.0 学习(06):Django 视图(进阶)
概述 Django中的特方法,该方法代表了Django的Web页面,并且视图具有特定的模板.以博客应用为例进行说明,在博客应用中应该包含下面的视图: 博客主页:显示最近的一些记录: 详细页面:单个详细 ...
- Django 2.0 学习(03):Django视图和URL(下)
接上篇博文,继续分析Django基本流程. 编写第一个(view)视图函数 1.打开文件polls/views.py,输入下面的Python代码: from django.http import Ht ...
- Django 2.0 学习(04):Django数据库
数据库设置/配置 打开mysite/settings.py,我们会发现Django是用的是默认的数据库SQLite,如下图所示: Django也是支持其它数据库的,比如PostgreSQL.MySQL ...
- Django 2.0 学习(12):Django 模板语法
Django 模板语法 一.模板 只要是在html里面有模板语法就不是html文件了,这样的文件就叫做模板. 二.模板语法 模板语法变量:{{ }} 在Django模板中遍历复杂数据结构的关键是句点字 ...
- Django 2.0 学习(08):Django 自动化测试
编写我们的第一个测试 确定bug 幸运的是,在polls应用中存在一个小小的bug急需修复:无论Question的发布日期是最近(最后)的日期,还是将来很多天的日期,Question.was_publ ...
- Django 2.0 学习(01):Django初识与安装
Django(Python Web框架) Django是一个开放源代码的Web框架,用Python写的.采用了MTV的框架模式,即模型M,模板T和视图V.它最初被开发是用来管理以新闻内容为主的网站,即 ...
- Django 2.0 学习
Django django是基于MTV结构的WEB框架 Model 数据库操作 Template 模版文件 View 业务处理 在Python中安装django 2.0 1 直接安装 pip inst ...
- Django 2.0 学习(19):Django 分页器
Django 分页器 要使用Django实现分页功能,必须从Django中导入Paginator模块(painator - 分页器) views.py from django.shortcuts im ...
- Django 2.0 学习(13):Django模板继承和静态文件
Django模板继承和静态文件 模板继承(extend) Django模板引擎中最强大也是最复杂的部分就是模板继承了,模板继承可以让我们创建一个基本的"骨架"模板,它可以包含网页中 ...
随机推荐
- CRL2.1更新
增加没有主键ID的抽象类,使能自义主键字段实现MODEL抽象类定义结构为 /// <summary> /// 基类,不包含任何字段 /// 如果有自定义主键名对象,请继承此类型 /// & ...
- git配置github链接
1.百度git官网-下载最新版git 2.一路默认下一步安装 3.打开 git bash here 命令行 4.注册github账号(用自己的邮箱就可以,不会英文可以用谷歌翻译)注册成功后建立项目 5 ...
- git基础(1)
一.获取git仓库(两种方法)1.现有目录初始化 git init目录有文件(非空文件)进行跟踪执行:git add+文件名提交:git commit -m(提交信息说明) 2.克隆现有代码仓库的代码 ...
- selenide 自动化测试进阶一: 查找元素和相关操作
基础环境配置和举例请移步:https://www.cnblogs.com/davice/p/9298742.html 提到自动化有些测试同学就会问,我会使用工具录制做自动化,我会用工具或插件识别元素. ...
- Linux命令应用大词典-第35章 终端
35.1 tty:显示当前连接到当前标准输入的终端设备文件名 35.2 consoletype:显示连接到标准输入的控制台类型 35.3 fgconsole:显示活动的虚拟终端数量 35.4 ming ...
- Java JDK5.0新特性
JDK5.0新特性 虽然JDK已经到了1.8 但是1.5(5.0)的变化是最大的 1. 增强for循环 foreach语句 foreach简化了迭代器 作用: 对存储对象的容器进行迭代 (数组, co ...
- Objective-C 第一个小程序
示例一 (类似C) //1.代码编写 //跟C语言一样,OC程序的入口依然是main函数,只不过写到一个.m文件中.比如这里写到一个main.m文件中(文件名可以是中文) #include <s ...
- 从零开始的Python学习Episode 1
一.输入与输出 1.输入 input("number:") num = input("number:") 下面一段可以把输入的信息存在num中. 注意:输入的信 ...
- java poi技术读取到数据库
https://www.cnblogs.com/hongten/p/java_poi_excel.html java的poi技术读取Excel数据到MySQL 这篇blog是介绍java中的poi技术 ...
- java超强分页标签演示
最近在做一个项目,用到了一个分页,于是动手写了个分页标签,先将代码贴出来,供大家交流,写的不好,请见谅!. 以下是java标签类,继承自SimpleTagSupport package com.lyn ...