Django 2.0 学习(06):Django 视图(进阶),我们将聚焦在使用简单的表单进行处理和精简代码。

编写简单表单

我们将用下面的代码,来替换之前的detail模板("polls/detail.html"):

  1. <h1>{{ question.question_text }}</h1>
  2. {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
  3. <form action="{% url 'polls:vote' question.id %}" method="post">
  4. {% csrf_token %}
  5. {% for choice in question.choice_set.all %}
  6. <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
  7. <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
  8. {% endfor %}
  9. <input type="submit" value="Vote" />
  10. </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,其代码如下:

  1. def vote(request, question_id):
  2. question = get_object_or_404(Question, pk=question_id)
  3. try:
  4. selected_choice = question.choice_set.get(pk=request.POST['choice'])
  5. except (KeyError, Choice.DoesNotExist):
  6. return render(request, 'polls/detail.html', {
  7. 'question': question,
  8. 'error_message': "You didn't select a choice."
  9. })
  10. else:
  11. selected_choice.votes += 1
  12. selected_choice.save()
  13. return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

上述代码涉及部分内容做如下解释:

  • request.POST是个像字典的对象,可以通过该对象的关键字(key)来获取提交数据。在这里,request.POST['choice']返回被选择选项的ID,request.POST返回值总是字符串;
  • 如果在POST数据中没有提供choicerequest.POST['choice']将会抛出KeyError异常。如果未提供choice,上述代码会检查KeyError并且通过一个错误信息来重新显示该问题;
  • 在增加选择计数后,代码返回HttpResponseRedirct而不是HttpResponse.HttpResponseRedirect携带一个单独的参数:用户被重定向到哪里的URL;作为优秀的Web开发,POST请求成功应该总是返回HttpResponseRedirect,这并不是Django特别要求的;
  • HttpResponseRedirect构造其中,我们使用了reverse()方法。该方法避免在视图函数中使用URL硬编码,他会提供我们想传递的视图函数的名字并且在ULR模式中找到该视图。

在有人对某个问题投票后,vote()视图会把结果重定向到结果页面,下面代码是完善的视图函数:

  1. def results(request, question_id):
  2. question = get_object_or_404(Question, pk=question_id)
  3. return render(request, 'polls/results.html', {'question': question})

现在创建polls/results.html模板文件:

  1. <h1>{{ question.question_text }}</h1>
  2. <ul>
  3. {% for choice in question.choice_set.all %}
  4. <li>
  5. {{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}
  6. </li>
  7. {% endfor %}
  8. </ul>
  9. <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"文件,修改其代码如下所示:

  1. from django.urls import path
  2. from . import views
  3. app_name = 'polls'
  4. urlpatterns = [
  5. path('', views.IndexView.as_view(), name='index'),
  6. path('<int:pk>/', views.DetailView.as_view(), name='detail'),
  7. path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
  8. path('<int:question_id>/vote/', views.vote, name='vote'),
  9. ]

注:在路径模型的第二、三个中,将question_id替换成了pk

其次,修改视图函数(使用Django的通用视图系统替换旧的indexdetailresults视图):打开polls/views.py文件,修改其代码如下所示:

  1. from django.shortcuts import get_object_or_404, render
  2. from django.http import HttpResponseRedirect
  3. from django.urls import reverse
  4. from django.views import generic
  5. from .models import Choice, Question
  6. class IndexView(generic.ListView):
  7. template_name = 'polls/index.html'
  8. context_object_name = 'latest_question_list'
  9. def get_queryset(self):
  10. return Question.objects.order_by('pub_date')[:5]
  11. class DetailView(generic.DetailView):
  12. model = Question
  13. template_name = 'polls/detail.html'
  14. class ResultsView(generic.DetailView):
  15. model = Question
  16. template_name = 'polls/results.html'
  17. def vote(request, question_id):
  18. question = get_object_or_404(Question, pk=question_id)
  19. try:
  20. selected_choice = question.choice_set.get(pk=request.POST['choice'])
  21. except (KeyError, Choice.DoesNotExist):
  22. return render(request, 'polls/detail.html', {
  23. 'question': question,
  24. 'error_message': "You didn't select a choice."
  25. })
  26. else:
  27. selected_choice.votes += 1
  28. selected_choice.save()
  29. return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
  30. # return HttpResponse("You're voting on question %s." % question_id)

我们这里使用两个通用视图:ListViewDetailView,这两个视图分别代表了"显示对象的列表"和"显示某个对象的详细信息"。

  • 使用模型属性,每个通用视图需要知道它所作用的模型;
  • 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 视图(进阶-续)的更多相关文章

  1. Django 2.0 学习(06):Django 视图(进阶)

    概述 Django中的特方法,该方法代表了Django的Web页面,并且视图具有特定的模板.以博客应用为例进行说明,在博客应用中应该包含下面的视图: 博客主页:显示最近的一些记录: 详细页面:单个详细 ...

  2. Django 2.0 学习(03):Django视图和URL(下)

    接上篇博文,继续分析Django基本流程. 编写第一个(view)视图函数 1.打开文件polls/views.py,输入下面的Python代码: from django.http import Ht ...

  3. Django 2.0 学习(04):Django数据库

    数据库设置/配置 打开mysite/settings.py,我们会发现Django是用的是默认的数据库SQLite,如下图所示: Django也是支持其它数据库的,比如PostgreSQL.MySQL ...

  4. Django 2.0 学习(12):Django 模板语法

    Django 模板语法 一.模板 只要是在html里面有模板语法就不是html文件了,这样的文件就叫做模板. 二.模板语法 模板语法变量:{{ }} 在Django模板中遍历复杂数据结构的关键是句点字 ...

  5. Django 2.0 学习(08):Django 自动化测试

    编写我们的第一个测试 确定bug 幸运的是,在polls应用中存在一个小小的bug急需修复:无论Question的发布日期是最近(最后)的日期,还是将来很多天的日期,Question.was_publ ...

  6. Django 2.0 学习(01):Django初识与安装

    Django(Python Web框架) Django是一个开放源代码的Web框架,用Python写的.采用了MTV的框架模式,即模型M,模板T和视图V.它最初被开发是用来管理以新闻内容为主的网站,即 ...

  7. Django 2.0 学习

    Django django是基于MTV结构的WEB框架 Model 数据库操作 Template 模版文件 View 业务处理 在Python中安装django 2.0 1 直接安装 pip inst ...

  8. Django 2.0 学习(19):Django 分页器

    Django 分页器 要使用Django实现分页功能,必须从Django中导入Paginator模块(painator - 分页器) views.py from django.shortcuts im ...

  9. Django 2.0 学习(13):Django模板继承和静态文件

    Django模板继承和静态文件 模板继承(extend) Django模板引擎中最强大也是最复杂的部分就是模板继承了,模板继承可以让我们创建一个基本的"骨架"模板,它可以包含网页中 ...

随机推荐

  1. 20145226夏艺华 《Java程序设计》第1周学习总结

    http://www.cnblogs.com/bestixyh/p/5779286.html 去年暑假写的,确实比较丑陋,保留下来也是为了激励自己作出更多改变.寒假写的每一篇博客都尽最大努力养成了良好 ...

  2. EnterpriseDB公司的 Postgres Solution Pack (一)

    下载地址: http://www.enterprisedb.com/products-services-training/products/postgres-plus-solution-pack/do ...

  3. 北京Uber优步司机奖励政策(4月8日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  4. Netty概述

    一,介绍 Netty是由JBOSS提供的一个java开源框架.Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序. 也就是说,Netty 是一 ...

  5. springboot jpa操作redis

    SpringBoot使用Redis缓存   (1)pom.xml引入jar包,如下: <dependency> <groupId>org.springframework.boo ...

  6. linux_fdisk命令详解,关于分区的详解

    这篇文章写的十分详细,特别的好 fdisk -l 可以列出所有的分区,包括没有挂上的分区和usb设备.我一般用这个来查找需要挂载的分区的位置,比如挂上u盘. 实例解说Linux中fdisk分区使用方法 ...

  7. hugepages_settings.sh

    #!/bin/bash## hugepages_settings.sh## Linux bash script to compute values for the# recommended HugeP ...

  8. CentOS下安装Tomcat环境

    一.安装JAVA环境 1.安装JAVA mkdir -p /usr/local/java 下载jdk1.7.0_67.tar.gz包,并解压到 tar xf jdk1.7.0_67.tar.gz -C ...

  9. 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域

    此篇文章是对上一篇文章(http://www.ifiero.com/index.php/archives/611)的进一步补充,主要说明如何适配Apple的最新三款手机iPhoneXs.iPhoneX ...

  10. mahout协同过滤算法各接口

    Mahout协同过滤算法 Mahout使用了Taste来提高协同过滤算法的实现,它是一个基于Java实现的可扩展的,高效的推荐引擎.Taste既实现了最基本的基于用户的和基于内容的推荐算法,同时也提供 ...