视图(View)是Django的MTV架构模式的V部分,主要负责处理用户请求和生成相应的响应内容然后在页面或其他类型文档中显示。也可以理解为视图是MVC架构里面的C部分(控制器),主要处理功能和业务上的逻辑。

构建网页内容

响应类型

在构建网页前,我们先了解响应类型,视图函数都是通过return方式返回数据内容的,然后生成相应的网页内容呈现在浏览器上。而视图函数的return具有多种响应类型,如下表所示。
image.png
响应类型代表HTTP状态码,其核心作用是Web Server服务器用来告诉客户端当前的网页请求发生了什么事,或者当前Web服务器的响应状态。上述响应主要来自于模块django.http,该模块是实现响应功能的核心。

实现文件下载

在实际开发中,可以使用该模板实现文件下载功能,在index的urls.py和views.py中分别添加以下代码:

  1. # urls.py
  2. path('download.html', views.download)
  3. # views.pypy
  4. def download(requset):
  5. response = HttpResponse(content_type='text/cssv')
  6. response['Content-Disposition'] = 'attachment; filename = "somefilename.csv"'
  7. writer = csv.writer(response)
  8. writer.writerow(['First row', 'A', 'B', 'C'])
  9. return response

在浏览器输入http://127.0.0.1:8000/download.html 后,用户发起访问页面的请求。当接收到用户的请求后,视图函数download首先定义HttpResponse的响应类型为文件(text/csv)类型,生成response对象。然后在response对象上定义Content-Disposition,设置浏览器下载文件的名称。attachment设置文件的下载方式,filename为文件名。最后使用CSV模块加载response对象,把数据写入response对象所设置的CSV文件并将response对象返回到浏览器上,从而实现文件下载。运行结果如图4所示。
image.png
image.png

采用render()响应

同理,除了能给用户响应文件外,一般响应的是用户请求的一个网页。针对上面的响应方式,Django对django.http模块上进行封装,从而有了render()、render_to_response()(该函数已开始被弃用)和redirect()函数。其中常用的就是render()函数了。
image.png
• request:浏览器向服务器发送的请求对象,包含用户信息、请求内容和请求方式等。
• template_name:HTML模板文件名,用于生成HTML网页。
• context:对HTML模板的变量赋值,以字典格式表示,默认情况下是一个空字典。
• content_type:响应数据的数据格式,一般情况下使用默认值即可。
• status:HTTP状态码,默认为200。
• using:设置HTML模板转换生成HTML网页的模板引擎。

实例—模仿华为商城

项目结构(index目录)
image.png
配置urls.py

  1. from django.urls import path,re_path
  2. from . import views
  3. urlpatterns = [
  4. path('', views.index),
  5. ]

视图view.spy添加视图函数index()

  1. from django.shortcuts import render
  2. # Create your views here.
  3. def index(request):
  4. return render(request, 'index.html',context={'title':'首页'},status=500)

加载静态资源:

  1. 获取链接:https://pan.baidu.com/s/1qHngl1lXDO1UFzsoabwOvQ
  2. 提取码:1v6f

编写网页:index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>{{ title }}</title>
  5. <meta charset="utf-8">
  6. {% load static %}
  7. <link rel="stylesheet" type="text/css" href="{% static "css/hw_index.css" %}" >
  8. <link rel="icon" href="{% static "img/hw.ico" %}">
  9. <script src="{% static "js/hw_index.js" %}"></script>
  10. </head>
  11. <body>
  12. <!-- 页面顶部 -->
  13. <!-- 顶部容器 :width:100% -->
  14. <header id="top">
  15. <!-- 内容显示区域 :width : 1211px -->
  16. <div id="top_box">
  17. <ul class="lf">
  18. <li><a href="#">华为官网</a></li>
  19. <li><a href="#">华为荣耀</a></li>
  20. </ul>
  21. <ul class="rt">
  22. <li><a href="#">登录</a></li>
  23. <li><a href="#">注册</a></li>
  24. <li><a href="#">我的订单</a></li>
  25. </ul>
  26. </div>
  27. </header>
  28. <!-- logo 和 搜索框 -->
  29. <div id="top_main">
  30. <div class="lf top_main_left">
  31. <img src="{% static "img/newLogo.png" %}" alt="" />
  32. </div>
  33. <div class="lf search_box">
  34. <div class="search">
  35. <input type="text" class="text" id="txtSearch"/>
  36. <input type="button" class="button" value="搜索"/>
  37. </div>
  38. <div class="hot_words">
  39. <span>热门搜索:</span>
  40. <a href="#"> 5C</a>
  41. <a href="#"> HUAWEI P9</a>
  42. <a href="#"> 5X</a>
  43. <a href="#">荣耀7</a>
  44. <a href="#"> Mate 8</a>
  45. </div>
  46. </div>
  47. <div class="lf" id="my_hw">我的商城</div>
  48. <div class="lf" id="settle_up">我的购物车</div>
  49. </div><!--top_main-->
  50. <!-- 导航 -->
  51. <nav id="nav">
  52. <div id="category">
  53. <a href="#">全部商品</a>
  54. </div>
  55. <ul id="nav_items" class="lf">
  56. <li><a href="#">首页</a></li>
  57. <li><a href="#">华为专区</a></li>
  58. <li><a href="#">荣耀专区</a></li>
  59. </ul>
  60. </nav>
  61. <!-- banner 广告 -->
  62. <div id="banner">
  63. {# <ul id="cate_box" class="lf">#}
  64. {# <li>#}
  65. {# <h3><a href="#">手机</a></h3>#}
  66. {# <p><span>荣耀</span><span>畅玩</span><span>华为</span><span>Mate/P系列</span></p>#}
  67. {# </li>#}
  68. {# <li>#}
  69. {# <h3><a href="#">平板 & 穿戴</a></h3>#}
  70. {# <p><span>平板电脑 </span><span>手环</span><span>手表</span></p>#}
  71. {# </li>#}
  72. {# </ul>#}
  73. <ul id="cate_box" class="lf">
  74. {% for type in type_list %}
  75. <li>
  76. <h3><a href="#">{{ type.type }}</a></h3>
  77. <p>
  78. {% for name in name_list %}
  79. {% if name.type == type.type %}
  80. <span>{{ name.name }}</span>
  81. {% endif %}
  82. {% endfor %}
  83. </p>
  84. </li>
  85. {% endfor %}
  86. </ul>
  87. <!--图片轮播 -->
  88. <form>
  89. <div class="slider">
  90. <img id="slider_img" class="img_slider" src="{% static "img/slide01.png" %}" alt=""/>
  91. </div>
  92. </form>
  93. </div>
  94. <div class="hr-2"></div>
  95. <!-- 网页主体 -->
  96. <section id="main">
  97. <div class="layout">
  98. <div class="fl u-4-3 lf">
  99. <ul>
  100. <li class="channel-pro-item">
  101. <!--<i class="p-tag"><img src="img/new_ping.png" style="padding-left: 0" alt=""/></i>-->
  102. <div class="p-img">
  103. <img src="{% static "img/phone01.png" %}" alt=""/>
  104. </div>
  105. <div class="p-name lf"><a href="#">HUAWEI P9 Plus</a></div>
  106. <div class="p-shining">
  107. <div class="p-slogan">一上手,就爱不释手</div>
  108. <div class="p-promotions">5月6日10:08 火爆开售</div>
  109. </div>
  110. <div class="p-price">
  111. <em>¥</em>
  112. <span>3988</span>
  113. </div>
  114. <div class="p-button lf">
  115. <a href="#">立即抢购</a>
  116. </div>
  117. </li>
  118. <li class="channel-pro-item" style="background-color:#E2F9FB"><!--荣耀畅玩5C-->
  119. <!--<i class="p-tag"><img src="img/new_ping02.png" alt=""/></i>-->
  120. <div class="p-img">
  121. <img src="{% static "img/phone02.png" %}" alt=""/>
  122. </div>
  123. <div class="p-name lf"><a href="#">荣耀畅玩5C</a></div>
  124. <div class="p-shining">
  125. <div class="p-slogan">16纳米8核芯千元普及风暴</div>
  126. <div class="p-promotions">5月10日10:08震撼开售</div>
  127. </div>
  128. <div class="p-price">
  129. <em>¥</em>
  130. <span>899</span>
  131. </div>
  132. <div class="p-button lf">
  133. <a href="#">立即抢购</a>
  134. </div>
  135. </li>
  136. <div class="hr-2"></div>
  137. <li class="channel-pro-item" style="background-color:#FFFCE7"><!--荣耀畅玩5C-->
  138. <!--<i class="p-tag"><img src="img/hot_but.png" alt=""/></i>-->
  139. <div class="p-img">
  140. <img src="{% static "img/phone05.png" %}" alt=""/>
  141. </div>
  142. <div class="p-name lf"><a href="#">荣耀7</a></div>
  143. <div class="p-shining">
  144. <div class="p-slogan">智灵键,创新语音控制</div>
  145. <div class="p-promotions">移动增强版原价1999直降200</div>
  146. </div>
  147. <div class="p-price">
  148. <em>¥</em>
  149. <span>1799</span>
  150. </div>
  151. <div class="p-button lf">
  152. <a href="#">立即抢购</a>
  153. </div>
  154. </li>
  155. <li class="channel-pro-item" style="background-color:#FFECEF"><!--荣耀畅玩5C-->
  156. <!--<i class="p-tag"><img src="img/hot_but.png" alt=""/></i>-->
  157. <div class="p-img">
  158. <img src="{% static "img/phone06.png" %}" alt=""/>
  159. </div>
  160. <div class="p-name lf"><a href="#">荣耀畅玩5X</a></div>
  161. <div class="p-shining">
  162. <div class="p-slogan">腾讯视频VIP特权免费领</div>
  163. <div class="p-promotions">5月4日至8日 每天10:08/20:00准点开售</div>
  164. </div>
  165. <div class="p-price">
  166. <em>¥</em>
  167. <span>899</span>
  168. </div>
  169. <div class="p-button lf">
  170. <a href="#">立即抢购</a>
  171. </div>
  172. </li>
  173. </ul>
  174. </div><!-- u-4-3结束-->
  175. <div class="lf fr u-4-1">
  176. <div class="channel-sale">
  177. <img src="{% static "img/phone03.jpg" %}" alt=""/>
  178. </div>
  179. <div class="hr-2"></div>
  180. <ul class="h-tab">
  181. <li class="current" id="tab-notice">
  182. <a href="#">公告</a>
  183. </li>
  184. <li id="tab-news" class="">
  185. <a href="#">评测 </a>
  186. </li>
  187. <div class="b">
  188. <ul id="tab-notice-content" style="display:block;">
  189. <li>
  190. <a href="#">HUAWEI G9 青春版震撼首发,全款预订赢好礼</a>
  191. </li>
  192. <li>
  193. <a href="#">荣耀畅玩5C,真芯不怕! 5月5日10:08震撼首发!</a>
  194. </li>
  195. <li>
  196. <a href="#">参与互动赢荣耀畅玩5C,与“胖红”一起怒FUN!</a>
  197. </li>
  198. <li>
  199. <a href="#">【中奖名单】HUAWEI WATCH星月系列购机抽奖</a>
  200. </li>
  201. </ul>
  202. </div>
  203. </ul>
  204. </div>
  205. <div class="hr-2"></div>
  206. <div class="banner">
  207. <img class="rt" src="{% static "img/vip.jpg" %}" alt=""/>
  208. <div class="hr-2"></div>
  209. </div>
  210. </div><!--layout HUAWEI P9 Plus结束-->
  211. </section>
  212. <div class="hr-2"></div>
  213. <div id="banner_img">
  214. <div class="banner-slideshow">
  215. <img src="{% static "img/p9.jpg" %}" width="100%" alt=""/>
  216. </div>
  217. </div>
  218. </div>
  219. <div class="hr-2"></div>
  220. <div class="hr-40"></div>
  221. <div class="slogan">
  222. <ul>
  223. <li class="s1"><i>500强企业 品质保证</i></li>
  224. <li class="s1"><i>7天退货 15天换货 </i></li>
  225. <li class="s1"><i>99元起免运费</i></li>
  226. <li class="s1"><i>448家维修网点 全国联保</i></li>
  227. </ul>
  228. </div>
  229. </body>
  230. </html>

完成效果:
image.png
除了render函数外,还有redirect()函数。redirect()函数用于实现请求重定向,重定向的链接以字符串的形式表示,链接的地址信息可以支持相对路径和绝对路径,代码如下:

  1. def login(request):
  2. return redirect('/')
  3. return redirect('http://127.0.0.1:8000/')

数据可视化

视图除了接收用户请求和返回响应内容之外,还可以与模型(Model)实现数据交互(操作数据库)。视图相当于一个处理中心,负责接收用户请求,然后根据请求信息读取并处理后台数据,最后生成HTML网页返回给用户。视图操作数据库实质是从models.py导入数据库映射对象。
通过在index目录下的models.py总添加:

  1. from django.db import models
  2. # Create your models here.
  3. class Product(models.Model):
  4. id = models.IntegerField(primary_key=True)
  5. name = models.CharField(max_length=50)
  6. type = models.CharField(max_length=20)

然后在命令输入下面的语句,将数据模型添加到数据库中。

  1. python manage.py makemigrations
  2. python manage.py migrate

image.png
添加数据如下:
image.png
完成数据表的数据添加后,接着将数据表的数据展现在网页上。首先将模板文件index.html左侧导航栏的代码注释掉,然后在同一位置添加Django的模板语法,代码如下:

  1. <ul id="cate_box" class="lf">
  2. {% for type in type_list %}
  3. <li>
  4. <h3><a href="#">{{type.type}}</a> </h3>
  5. <p>
  6. {% for name in name_list %}
  7. {% if name.type == type.type %}
  8. <span>{{name.name }}</span>
  9. {% endif %}
  10. {% endfor %}
  11. </p>
  12. </li>
  13. {% endfor %}
  14. </ul>

此外,修改views里面index视图函数字典传入的内容:

  1. def index(request):
  2. type_list = Product.objects.values('type').distinct()
  3. name_list = Product.objects.values('name','type')
  4. title = '首页'
  5. return render(request, 'index.html',context=locals(),status=200)

刷新页面,导航栏显示出下面的信息

image.png

获取请求信息

视图是用于接收并处理用户的请求信息,请求信息存放在视图函数的参数request中,下面给出request的常用属性。
image.png
上述属性中的GET、POST和method是每个Web开发人员必须掌握的基本属性,属性GET和POST用于获取用户的请求参数,属性method用于获取用户的请求方式。以视图函数login为例,修改视图函数login()如下:

  1. def login(request):
  2. if request.method == 'POST':
  3. name = request.POST.get('name')
  4. else:
  5. if request.GET.get('name') and request.GET.get('password'):
  6. name = request.GET.get('name')
  7. password = request.GET.get('password')
  8. else:
  9. name = 'Everyone'
  10. password = '00000'
  11. return HttpResponse('username is: ' + name + '<br> password is:' + password)
  12. # return redirect('/')
  13. # return redirect('http://127.0.0.1:8000/')

image.pngimage.png

第二条URL地址多出了?name=Tom,这是GET请求的请求参数。GET请求参数以?为标识,请求参数以等值的形式表示,等号前面的是参数名,后面的是参数值,如果涉及多个参数,每个参数之间用&拼接。

通用视图

为了避免编写视图的无聊好单调,Django植入了通用视图这一功能,通用视图是通过定义和声明类的形式实现的,根据用途划分三大类:TemplateView、ListView和DetailView。
• TemplateView直接返回HTML模板,但无法将数据库的数据展示出来。
• ListView能将数据库的数据传递给HTML模板,通常获取某个表的所有数据。
• DetailView能将数据库的数据传递给HTML模板,通常获取数据表的单条数据。
在数据可视化项目的基础上,将其视图函数改用ListView实现。本例子沿用index.html模板文件,然后在urls.py中添加URL地址信息,代码如下:

  1. urls.py
  2. from django.urls import path,re_path
  3. from . import views
  4. urlpatterns = [
  5. path('', views.index),
  6. path('login.html', views.login),
  7. path('index/', views.ProductList.as_view()) # 新增
  8. ]
  9. # views.py 中定义新的视图类ProductList
  10. class ProductList(ListView):
  11. context_object_name = 'type_list'
  12. template_name = 'index.html'
  13. queryset = Product.objects.values('type').distinct()
  14. def get_queryset(self):
  15. type_list = Product.objects.values('type').distinct()
  16. return type_list
  17. def get_context_data(self, *, object_list=None, **kwargs):
  18. context = super().get_context_data(**kwargs)
  19. context['name_list'] = Product.objects.values('name','type')
  20. return context

通用视图ProductList的代码说明如下:
• 定义ProductList类,该类继承自ListView类,具有ListView的所有特性。
• context_object_name设置HTML模板的变量。
• template_name设置HTML模板。
• queryset查询数据库数据,查询结果会赋值给context_object_name所设置的变量。
• 重写函数get_queryset,该函数的功能与queryset实现的功能一致。
• 重写函数get_context_data,该函数设置HTML模板的其他变量。
通用视图的代码编写规则有一定的固定格式,根据这个固定格式可以快速开发数据视图。除此之外,通用视图还可以获取URL的参数和请求信息,使得通用视图更加灵活,以get_queryset函数为例:

  1. from django.urls import path,re_path
  2. from . import views
  3. urlpatterns = [
  4. path('', views.index),
  5. path('login.html', views.login),
  6. path('index/', views.ProductList.as_view()),
  7. path('index/<id>.html', views.ProductList.as_view(), {'name': '手机'})# 修改urls.py
  8. ]
  9. def get_queryset(self):
  10. # 下面的 id ,name 都从url中获取得到
  11. print(self.kwargs['id'])
  12. print(self.kwargs['name'])
  13. print(self.request.method)
  14. type_list = Product.objects.values('type').distinct()
  15. return type_list

image.png
从上面的例子可以看出,通用视图的代码量感觉比视图函数多,但是通用视图是可以被继承的。假如已经写好了一个基于类的通用视图,若要对其添加拓展功能,只需继承原本这个类即可。

参考文献:玩转Django 2.0/黄永祥著.-北京:清华大学出版社,2018