05 Django 视图层views.py
一、视图层简介
都看到这里了,视图层有何卵用就不用我在过多介绍了吧!!!
视图层是干嘛的呢?:放视图函数的,简称视图
提示:视图层是用来放路径对应的视图函数的!!!
二、视图函数
视图函数,简称视图,属于Django的视图层,默认定义在views.py文件中。
是用来处理web请求信息以及返回响应信息的函数,所以研究视图函数只需要掌握两个对象即可。
请求对象(HttpRequest)
响应对象(HttpResponse)
三、请求对象(HttpRequest)
Django将http协议请求数据报中的请求头、请求首行、\r\n、请求内容主体封装到了HttpRequest对象中(类似于我们自定义web框架的env参数)。Django会将HttpRequest 对象当作参数传给视图函数的第一个参数request,在视图函数中,通过访问该对象的属性便可以提取http协议的请求数据
3.1 HttpRequest 请求对象的常用属性----01部分
一、HttpRequest.method
获取浏览器访问服务器的请求方式(GET/POST)
注意:值为纯大写的字符串格式 'GET' 'POST'
作用:通过该属性的值来判断浏览器对服务器的请求方式
二、HttpRequest.GET
值为一个类似于字典的QueryDict对象,封装了GET请求的所有参数
可通过HttpRequest.GET.get('请求到后端的键值(key)') 获取相应的值value
三、HttpRequest.POST
(这个一般用于表单中)
值为一个类似于字典的QueryDict对象,封装了GET请求的所有参数
可通过HttpRequest.POST.get('请求到后端的键值(key)') 获取相应的值value
注意:针对表单中checkbox复选框类型的input标签、select标签提交到后端的数据,键对应的值为多个,比如需要用:HttpRequest.POST.getlist('hobbies(key键)')获取存有多个值的列表。
3.2 常用属性案例----01部分
# urls.py路由表文件
from django.urls import url
from app01 import views
urlpatterns = [
url(r'^login/$',views.login,name='login_page'),
]
# views.py 视图文件
from django.shortcuts import render,HttpResponse
def login(request):
# 利用request的method属性来判断浏览器的请求方式
if request.method=='GET':
# 如果是get请求,请求的参数都是存放在request.GET中
print(request.GET)
# 返回的是字典{key:values......},并且可以按照字典取值
return render('login_page')
# 如果是POST请求
# 在form表单中的输入框输入用户名cecilia、密码123,选择爱好、点击提交
# 表单内的数据都会存放于request.POST中
print(request.POST) # 返回的是一个字典对象{key:values......}
# 获取表单中的数据方式为:
name = request.POST.get('name') #cecilia
pwd = request.POST.get('pwd') #123
hobbies = request.POST.get('hobbies') #[......]
return HttpResponse('提交成功!')
# login.html文件
from django.shortcuts import render, HttpResponse
def login(request):
# 利用request的method属性来判断浏览器的请求方式
if request.method == 'GET':
# 如果是get请求,请求的参数都是存放在request.GET中
print(request.GET)
# 返回的是字典{key:values......},并且可以按照字典取值
return render(request,'login.html')
# 如果是POST请求
# 在form表单中的输入框输入用户名cecilia、密码123,选择爱好、点击提交
# 表单内的数据都会存放于request.POST中
print(request.POST) # 返回的是一个字典对象{key:values......}
# 获取表单中的数据方式为:
name = request.POST.get('name') # cecilia
pwd = request.POST.get('pwd') # 123
hobbies = request.POST.get('hobbies') # [......]
return HttpResponse('提交成功!')
四、HttpResponse(响应对象)
响应对象是服务器返回给浏览器的信息,响应信息可以是任何形式的内容。
比如一个Html页面的内容,一个重定向的内容(可以是第三方的),一个404错误、一个xml文档,或者是一张图片等。总之,无论视图本身包含什么逻辑,都要返回响应,具体的说,响应对象主要有三中形式:(记住:django所有的响应信息的方式,他们都是属于一个HttpResponse对象)
from django.shortcuts import render # 返回html页面
from django.shortcuts import HttpResponse # 返回一个字符串,
from django.shortcuts import redirect # 重定向
4.1 HttpResponse() 响应字符串
括号内直接写一个具体的字符串作为响应体,比较直接简单,所以这里主要介绍后面两种形式
4.2 render() 响应html页面
render(request,templates_name[,context])
参数:
1. request:用于生成响应的请求对象,固定必须传入的第一个参数
2.templates_name:要使用的模板的完整名称,必须传入,render默认去templates文件夹下找这个html页面(模板文件)
3.context:可选的参数,可以传入一个字典用来代替模块文件中的变量
# 案例
def test(request):
res = Template('<h1>{{user}}</h1>') # 这里输入一个字符串,这里支持模板语法
con = Context({'user':{'user':'cecilia','age':18}}) # 这里输入的是一个字典
ret = res.render(con) # 这个rerder会看con里面有没有user,有的话返回给ret,是个字符串
print(ret)
return HttpResponse(ret)
# 此时在浏览器访问对应的url会响应一传字符串渲染页面
总结:render的功能可以总结为:根据给定字典渲染模板文件,并返回渲染后的HttpResponse对象
4.3 redirect() 重定向
# 返回重定向信息
def index(request):
return redirect('login/')
# 重定向的地址也可以是一个完整的url
def index(request):
return redirect('http://www.baidu.com/')
4.4 重定向状态码301与302的区别(了解)
一、301 和 302 的异同
1.相同之处:
301和302状态码都是表示重定向,具体的说就是浏览器在拿到服务器返回的这个状态码后会自动跳转到一个显得url地址(浏览器会从响应头location中获取新地址),用户看到的效果都是输入地址a后瞬间跳转到了另外一个地址B
2.不同之处
301表示就地址A的资源已经被永久的移除了,即这个资源不可以在访问了,搜索引擎在抓取新内容的同时也将旧的网址转换为重定向的地址
302表示旧地址A的资源还在,即这个资源任然可以访问,这个重定向知识临时的从旧地址A跳转到地址B,搜索引擎会抓取新的内容、并且会保存旧的网址。从SEO层面考虑,302要好于301
二、重定向原因:
1.网站的调整(如改变网页的目录结构)
2.网页被移到一个新地址
3.网页扩展名改变(如应用需要把.php改成.Html或.shtml).
这种情况下,如果不做重定向吗,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户得到一个404页面错误信息,访问流量白白的丧失;再者某些注册了多个域名的网站,也需要通过重定向让访问这些域名的用户自动跳转到主站点
五、JsonResponse(返回json字符串)
json数据类型格式特点:跨平台、跨语言
为什么要给前端返回json字符串呢?
因为我们的项目需要前后端分离的话,是基于json语言数据格式传输数据的。。。。。
前端:利用序列化/反序列化转换成前端对应的数据格式
# 前端序列化和反序列化用法
json.stringify------>对应后端的json.dumps()序列化
json.parse---------->对应后端的json.loads()反序列化
实例:
from django.http import JsonResponse
import json # 先导入json模块
# json模块序列化
def my_json(request):
data1 = {'user':'我是cecilia','pwd':'123'}
# 这个字典中有中文,json模块默认在序列化的时候,会将中文转码为2进制的形式
# 这里需要设置一个参数ensure_ascii=False,中文就不会被转码了
return HttpResponse(json.dumps(data1,ensure_ascii=False))
def my_json(request):
data = {'user':'xichen','age':18}
res = json.dumps(data)# 这里data这个字典已经被序列化为一个json格式的字符串数据了
return Httpsponse(res)
注意:
import json
from django.http import JsonResponse
# 注意点 1
def my_view(request):
data = ['cecilia','xichen']
return HttpResponse(json.dumps(data))
# 注意点 2
def my_view(request):
data = ['cecilia','xichen']
return JsonResponse(data,safe=False)
# 默认safe=True代表只能序列化字典对象,safe=False代表可以序列化字典以外的对象
实现将日期型数据序列化为json数据格式:
# 在python中json序列化数据格式只有八种类型,不包括日期型
import json
from datetime import datetime
class mysite(json.JSONEncoder): # 自己重新写一个类,继承json.JSONEncoder
# 在类里重写default这个方法
def default(self, o): # 这个o是下面我们
if isinstance(o,datetime):
return o.strftime('%Y-%m-%d')
else:
super().default(self,o)
d = {'ctime':datetime.today()}
print(json.dumps(d,cls=mysite))
六、Form表单上传文件
form表单上传文件/图片需要注意:form 表单的提交方式要设置为post,enctype属性="multipart/form-data"
这里提一个命令:在下面获取文件对象的所有方法以及属性dir命令
'''form 表单上传文件/图像并保存在本地'''
def test(request):
if request.method == 'GET':
return render(request,'test.html')
print(request.POST)
# < QueryDict: {'myfile': ['1.png']} >
# 获取文件数据的时候,不会讲文件数据放在post里面
print(request.FILES) # 这个时候就有数据了
# < MultiValueDict: {'myfile': [ < InMemoryUploadedFile: 1.png(image / png) >]} >
# 由于提交表单的时候,我们给上传文件设置的name属性为myfile
file_obj = request.FILES.get('myfile') # 获取文件对象
print(dir(file_obj)) # 打印这个文件对象的所有可以.的属性和方法
# file_obj.name 是一个对象获取名字,作为路径
with open(file_obj.name,'wb') as f:
for line in file_obj: # file_obj 相当于是文件句柄
f.write(line) # 然后浏览器中选择文件,提交就可以把这个文件或者图像存起来的
# 这里是官方推荐的写法
# for chunks in file_obj.chunks():
# f.write(chunks)
return render(request,'test.html')
七、FBV和CBV
django的视图层由两种形式构成:FBV和CBV
- FBV基于函数的视图(function base view),我们之前一直介绍的都是FBV
- CBV基于类的视图(class base view)
# urls.py路由文件
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 测试CBV下的视图
url(r'^login/',views.loginview.as_view()),# 这里必须调用类下的方法
]
# views.py视图文件
class loginview(View):
def dispatch(self,request,*args,**kwargs): # 可在该方法内做一些预处理操作
# 当请求url:http://127.0.0.1:8001/login/会触发dispatch的执行
# 如果http协议的请求方法为GET,则调用下述get方法
# 如果http协议的请求方法为POST,则调用下述post方法
obj = super().dispatch(request,*args,**kwargs)# 必须继承父类的dispatch功能
return obj # 必须返回obj
def get(self,request):
return render(request,'login.html')
def post(self,request):
name = request.POST.get('name')
pwd = request.POST.get('pwd')
if name == 'cecilia' and pwd == '123':
res = '登录成功'
else:
res = '用户密码错误'
return HttpResponse(res)
测试:
python manage.py runserver 8001
# 验证GET请求:在浏览器种输入HTTP://127.0.0.1:8001/login/
# 验证POST请求:在login页面的表单种输入数据然后提交
八、 CBV分析
基于以上的列子,我们来做CBV执行的流程分析
第一步:路由层中的url(r'^login/',views.loginview.as_view()) 视图对应关系,
首先会找到views视图层中的对应的loginview类的as_view()方法
第二部:由于自己写的loginview类中并没有这个as_view()方法,但是我们在写这个类时继承了一个Views类,所以根据类的属性查找顺序找到了父类Views类中的as_view()方法,看下图在执行as_view()方法时发生的事情
第三步:执行完as_view()函数后拿到内层函数view的内存地址返回给调用者,也就是我们的loginview类,这时,我们的路由层与视图函数对应关系可以变化为url(r'^login/',views.view) # 在这里,我们看出来了,这和FBV的路由没什么不同
第四步:这个时候执行视图层views.py中的view,而这个view我们并没有,这时又是在我们自定义的loginview类中找,发现也没有,按照属性查找顺序去父类中,当然可以找到,在view函数的流程如下:
第五步:我们依然很懵逼,这个对象有这个dispatch方法???貌似好像没有,这是我们自己写的类实例化出来的对象,所以按属性查找顺序去找,依然找到了自己写的类继承的父类views类中的dispatch方法,内部流程如下图:
总结:在这里哈哈哈,CBV源码分析完了,是不是感觉一脸懵逼呢? 此处建议只看以上三张图多看看还是没任何问题的呢!!!