视图(View)是Django的MTV架构模式的V部分,主要负责处理用户请求和生成相应的响应内容,然后在页面或其他类型文档中显示。也可以理解为视图是MVC架构里面的C部分(控制器),主要处理功能和业务上的逻辑。
构建网页内容
响应类型
在构建网页前,我们先了解响应类型,视图函数都是通过return方式返回数据内容的,然后生成相应的网页内容呈现在浏览器上。而视图函数的return具有多种响应类型,如下表所示。
响应类型代表HTTP状态码,其核心作用是Web Server服务器用来告诉客户端当前的网页请求发生了什么事,或者当前Web服务器的响应状态。上述响应主要来自于模块django.http,该模块是实现响应功能的核心。
实现文件下载
在实际开发中,可以使用该模板实现文件下载功能,在index的urls.py和views.py中分别添加以下代码:
# urls.py
path('download.html', views.download)
# views.pypy
def download(requset):
response = HttpResponse(content_type='text/cssv')
response['Content-Disposition'] = 'attachment; filename = "somefilename.csv"'
writer = csv.writer(response)
writer.writerow(['First row', 'A', 'B', 'C'])
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所示。
采用render()响应
同理,除了能给用户响应文件外,一般响应的是用户请求的一个网页。针对上面的响应方式,Django对django.http模块上进行封装,从而有了render()、render_to_response()(该函数已开始被弃用)和redirect()函数。其中常用的就是render()函数了。• request:浏览器向服务器发送的请求对象,包含用户信息、请求内容和请求方式等。
• template_name:HTML模板文件名,用于生成HTML网页。
• context:对HTML模板的变量赋值,以字典格式表示,默认情况下是一个空字典。
• content_type:响应数据的数据格式,一般情况下使用默认值即可。
• status:HTTP状态码,默认为200。
• using:设置HTML模板转换生成HTML网页的模板引擎。
实例—模仿华为商城
项目结构(index目录)
配置urls.py
from django.urls import path,re_path
from . import views
urlpatterns = [
path('', views.index),
]
视图view.spy添加视图函数index()
from django.shortcuts import render
# Create your views here.
def index(request):
return render(request, 'index.html',context={'title':'首页'},status=500)
加载静态资源:
获取链接:https://pan.baidu.com/s/1qHngl1lXDO1UFzsoabwOvQ
提取码:1v6f
编写网页:index.html
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<meta charset="utf-8">
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static "css/hw_index.css" %}" >
<link rel="icon" href="{% static "img/hw.ico" %}">
<script src="{% static "js/hw_index.js" %}"></script>
</head>
<body>
<!-- 页面顶部 -->
<!-- 顶部容器 :width:100% -->
<header id="top">
<!-- 内容显示区域 :width : 1211px -->
<div id="top_box">
<ul class="lf">
<li><a href="#">华为官网</a></li>
<li><a href="#">华为荣耀</a></li>
</ul>
<ul class="rt">
<li><a href="#">登录</a></li>
<li><a href="#">注册</a></li>
<li><a href="#">我的订单</a></li>
</ul>
</div>
</header>
<!-- logo 和 搜索框 -->
<div id="top_main">
<div class="lf top_main_left">
<img src="{% static "img/newLogo.png" %}" alt="" />
</div>
<div class="lf search_box">
<div class="search">
<input type="text" class="text" id="txtSearch"/>
<input type="button" class="button" value="搜索"/>
</div>
<div class="hot_words">
<span>热门搜索:</span>
<a href="#"> 5C</a>
<a href="#"> HUAWEI P9</a>
<a href="#"> 5X</a>
<a href="#">荣耀7</a>
<a href="#"> Mate 8</a>
</div>
</div>
<div class="lf" id="my_hw">我的商城</div>
<div class="lf" id="settle_up">我的购物车</div>
</div><!--top_main-->
<!-- 导航 -->
<nav id="nav">
<div id="category">
<a href="#">全部商品</a>
</div>
<ul id="nav_items" class="lf">
<li><a href="#">首页</a></li>
<li><a href="#">华为专区</a></li>
<li><a href="#">荣耀专区</a></li>
</ul>
</nav>
<!-- banner 广告 -->
<div id="banner">
{# <ul id="cate_box" class="lf">#}
{# <li>#}
{# <h3><a href="#">手机</a></h3>#}
{# <p><span>荣耀</span><span>畅玩</span><span>华为</span><span>Mate/P系列</span></p>#}
{# </li>#}
{# <li>#}
{# <h3><a href="#">平板 & 穿戴</a></h3>#}
{# <p><span>平板电脑 </span><span>手环</span><span>手表</span></p>#}
{# </li>#}
{# </ul>#}
<ul id="cate_box" class="lf">
{% for type in type_list %}
<li>
<h3><a href="#">{{ type.type }}</a></h3>
<p>
{% for name in name_list %}
{% if name.type == type.type %}
<span>{{ name.name }}</span>
{% endif %}
{% endfor %}
</p>
</li>
{% endfor %}
</ul>
<!--图片轮播 -->
<form>
<div class="slider">
<img id="slider_img" class="img_slider" src="{% static "img/slide01.png" %}" alt=""/>
</div>
</form>
</div>
<div class="hr-2"></div>
<!-- 网页主体 -->
<section id="main">
<div class="layout">
<div class="fl u-4-3 lf">
<ul>
<li class="channel-pro-item">
<!--<i class="p-tag"><img src="img/new_ping.png" style="padding-left: 0" alt=""/></i>-->
<div class="p-img">
<img src="{% static "img/phone01.png" %}" alt=""/>
</div>
<div class="p-name lf"><a href="#">HUAWEI P9 Plus</a></div>
<div class="p-shining">
<div class="p-slogan">一上手,就爱不释手</div>
<div class="p-promotions">5月6日10:08 火爆开售</div>
</div>
<div class="p-price">
<em>¥</em>
<span>3988</span>
</div>
<div class="p-button lf">
<a href="#">立即抢购</a>
</div>
</li>
<li class="channel-pro-item" style="background-color:#E2F9FB"><!--荣耀畅玩5C-->
<!--<i class="p-tag"><img src="img/new_ping02.png" alt=""/></i>-->
<div class="p-img">
<img src="{% static "img/phone02.png" %}" alt=""/>
</div>
<div class="p-name lf"><a href="#">荣耀畅玩5C</a></div>
<div class="p-shining">
<div class="p-slogan">16纳米8核芯千元普及风暴</div>
<div class="p-promotions">5月10日10:08震撼开售</div>
</div>
<div class="p-price">
<em>¥</em>
<span>899</span>
</div>
<div class="p-button lf">
<a href="#">立即抢购</a>
</div>
</li>
<div class="hr-2"></div>
<li class="channel-pro-item" style="background-color:#FFFCE7"><!--荣耀畅玩5C-->
<!--<i class="p-tag"><img src="img/hot_but.png" alt=""/></i>-->
<div class="p-img">
<img src="{% static "img/phone05.png" %}" alt=""/>
</div>
<div class="p-name lf"><a href="#">荣耀7</a></div>
<div class="p-shining">
<div class="p-slogan">智灵键,创新语音控制</div>
<div class="p-promotions">移动增强版原价1999直降200</div>
</div>
<div class="p-price">
<em>¥</em>
<span>1799</span>
</div>
<div class="p-button lf">
<a href="#">立即抢购</a>
</div>
</li>
<li class="channel-pro-item" style="background-color:#FFECEF"><!--荣耀畅玩5C-->
<!--<i class="p-tag"><img src="img/hot_but.png" alt=""/></i>-->
<div class="p-img">
<img src="{% static "img/phone06.png" %}" alt=""/>
</div>
<div class="p-name lf"><a href="#">荣耀畅玩5X</a></div>
<div class="p-shining">
<div class="p-slogan">腾讯视频VIP特权免费领</div>
<div class="p-promotions">5月4日至8日 每天10:08/20:00准点开售</div>
</div>
<div class="p-price">
<em>¥</em>
<span>899</span>
</div>
<div class="p-button lf">
<a href="#">立即抢购</a>
</div>
</li>
</ul>
</div><!-- u-4-3结束-->
<div class="lf fr u-4-1">
<div class="channel-sale">
<img src="{% static "img/phone03.jpg" %}" alt=""/>
</div>
<div class="hr-2"></div>
<ul class="h-tab">
<li class="current" id="tab-notice">
<a href="#">公告</a>
</li>
<li id="tab-news" class="">
<a href="#">评测 </a>
</li>
<div class="b">
<ul id="tab-notice-content" style="display:block;">
<li>
<a href="#">HUAWEI G9 青春版震撼首发,全款预订赢好礼</a>
</li>
<li>
<a href="#">荣耀畅玩5C,真芯不怕! 5月5日10:08震撼首发!</a>
</li>
<li>
<a href="#">参与互动赢荣耀畅玩5C,与“胖红”一起怒FUN!</a>
</li>
<li>
<a href="#">【中奖名单】HUAWEI WATCH星月系列购机抽奖</a>
</li>
</ul>
</div>
</ul>
</div>
<div class="hr-2"></div>
<div class="banner">
<img class="rt" src="{% static "img/vip.jpg" %}" alt=""/>
<div class="hr-2"></div>
</div>
</div><!--layout HUAWEI P9 Plus结束-->
</section>
<div class="hr-2"></div>
<div id="banner_img">
<div class="banner-slideshow">
<img src="{% static "img/p9.jpg" %}" width="100%" alt=""/>
</div>
</div>
</div>
<div class="hr-2"></div>
<div class="hr-40"></div>
<div class="slogan">
<ul>
<li class="s1"><i>500强企业 品质保证</i></li>
<li class="s1"><i>7天退货 15天换货 </i></li>
<li class="s1"><i>99元起免运费</i></li>
<li class="s1"><i>448家维修网点 全国联保</i></li>
</ul>
</div>
</body>
</html>
完成效果:
除了render函数外,还有redirect()函数。redirect()函数用于实现请求重定向,重定向的链接以字符串的形式表示,链接的地址信息可以支持相对路径和绝对路径,代码如下:
def login(request):
return redirect('/')
return redirect('http://127.0.0.1:8000/')
数据可视化
视图除了接收用户请求和返回响应内容之外,还可以与模型(Model)实现数据交互(操作数据库)。视图相当于一个处理中心,负责接收用户请求,然后根据请求信息读取并处理后台数据,最后生成HTML网页返回给用户。视图操作数据库实质是从models.py导入数据库映射对象。
通过在index目录下的models.py总添加:
from django.db import models
# Create your models here.
class Product(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=50)
type = models.CharField(max_length=20)
然后在命令输入下面的语句,将数据模型添加到数据库中。
python manage.py makemigrations
python manage.py migrate
添加数据如下:
完成数据表的数据添加后,接着将数据表的数据展现在网页上。首先将模板文件index.html左侧导航栏的代码注释掉,然后在同一位置添加Django的模板语法,代码如下:
<ul id="cate_box" class="lf">
{% for type in type_list %}
<li>
<h3><a href="#">{{type.type}}</a> </h3>
<p>
{% for name in name_list %}
{% if name.type == type.type %}
<span>{{name.name }}</span>
{% endif %}
{% endfor %}
</p>
</li>
{% endfor %}
</ul>
此外,修改views里面index视图函数字典传入的内容:
def index(request):
type_list = Product.objects.values('type').distinct()
name_list = Product.objects.values('name','type')
title = '首页'
return render(request, 'index.html',context=locals(),status=200)
获取请求信息
视图是用于接收并处理用户的请求信息,请求信息存放在视图函数的参数request中,下面给出request的常用属性。
上述属性中的GET、POST和method是每个Web开发人员必须掌握的基本属性,属性GET和POST用于获取用户的请求参数,属性method用于获取用户的请求方式。以视图函数login为例,修改视图函数login()如下:
def login(request):
if request.method == 'POST':
name = request.POST.get('name')
else:
if request.GET.get('name') and request.GET.get('password'):
name = request.GET.get('name')
password = request.GET.get('password')
else:
name = 'Everyone'
password = '00000'
return HttpResponse('username is: ' + name + '<br> password is:' + password)
# return redirect('/')
# return redirect('http://127.0.0.1:8000/')
第二条URL地址多出了?name=Tom,这是GET请求的请求参数。GET请求参数以?为标识,请求参数以等值的形式表示,等号前面的是参数名,后面的是参数值,如果涉及多个参数,每个参数之间用&拼接。
通用视图
为了避免编写视图的无聊好单调,Django植入了通用视图这一功能,通用视图是通过定义和声明类的形式实现的,根据用途划分三大类:TemplateView、ListView和DetailView。
• TemplateView直接返回HTML模板,但无法将数据库的数据展示出来。
• ListView能将数据库的数据传递给HTML模板,通常获取某个表的所有数据。
• DetailView能将数据库的数据传递给HTML模板,通常获取数据表的单条数据。
在数据可视化项目的基础上,将其视图函数改用ListView实现。本例子沿用index.html模板文件,然后在urls.py中添加URL地址信息,代码如下:
urls.py
from django.urls import path,re_path
from . import views
urlpatterns = [
path('', views.index),
path('login.html', views.login),
path('index/', views.ProductList.as_view()) # 新增
]
# views.py 中定义新的视图类ProductList
class ProductList(ListView):
context_object_name = 'type_list'
template_name = 'index.html'
queryset = Product.objects.values('type').distinct()
def get_queryset(self):
type_list = Product.objects.values('type').distinct()
return type_list
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(**kwargs)
context['name_list'] = Product.objects.values('name','type')
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函数为例:
from django.urls import path,re_path
from . import views
urlpatterns = [
path('', views.index),
path('login.html', views.login),
path('index/', views.ProductList.as_view()),
path('index/<id>.html', views.ProductList.as_view(), {'name': '手机'})# 修改urls.py
]
def get_queryset(self):
# 下面的 id ,name 都从url中获取得到
print(self.kwargs['id'])
print(self.kwargs['name'])
print(self.request.method)
type_list = Product.objects.values('type').distinct()
return type_list
从上面的例子可以看出,通用视图的代码量感觉比视图函数多,但是通用视图是可以被继承的。假如已经写好了一个基于类的通用视图,若要对其添加拓展功能,只需继承原本这个类即可。
参考文献:玩转Django 2.0/黄永祥著.-北京:清华大学出版社,2018