Django之视图层

文章目录

  • 视图函数的返回值
    • 为什么视图函数需要返回一个HttpResponse对象?
    • 三板斧源码大概注解
    • 三板斧的使用
      • HttpResponse
      • redirect
      • render
  • JsonResponse对象
    • 视图函数返回json格式的数据
  • form表单携带文件数据
  • request对象方法
  • FBV与CBV
    • FBV
    • CBV
  • CBV的源码解析

前言

一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你的Python目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中。

django视图层:Django项目下的views.py文件,它的内部是一系列的函数或者是类,用来处理客户端的请求后处理并返回相应的数据

视图函数的返回值

为什么视图函数需要返回一个HttpResponse对象?

可以用一个例子来看看:(前提先配置好路由)

Django之视图层_第1张图片

	def index(request):
		return None

然后我们直接开启服务,访问这个路由地址
Django之视图层_第2张图片

最终得到的结果是,视图函数必须要返回一个HttpResponse对象


三板斧源码大概注解

当浏览器向服务器发起数据请求的时候,那么服务器用来响应的视图函数的返回值的种类有两种,分别是HTML响应(HttpResponse、render、redirect)和JsonResponse(前后端分离)

而其中HTML的三种响应方式返回的都是一个HttpResponse对象(可以查看源码)

我们可以通过源码查看到这三个响应方式,都最后都返回了HttpResponse对象,类名加括号产生一个对象,所以返回的都是对象

	'''源码大概注解展示'''
	
	class HttpResponse(HttpResponseBase):
		'''括号内直接跟一个具体的字符串作为响应体'''
	    streaming = False  # 表示是否支持流式传输
	    
	    # 初始化HttpResponse实例,设置响应的内容
	    def __init__(self, content=b'', *args, **kwargs):
	        super(HttpResponse, self).__init__(*args, **kwargs)
	        # Content is a bytestring. See the `content` property methods.
	        self.content = content
	    ......

	====================================================================================
	
	def render(request, template_name, context=None, content_type=None, status=None, using=None):
	'''
		request:用于生成响应的请求对象
		template_name:要使用的模版的完整名称,可选的参数
		context:添加到模版上细纹的一个字典。默认是空字典。如果字典中的某一个值是可调用的,视图将在渲染模版之前调用它。
		render方法就是将一个模版页面中的模版语法进行渲染,最终渲染成一个html页面作为响应体
	'''
		# 使用Django的模版引擎加载和渲染模版
	    content = loader.render_to_string(template_name, context, request, using=using)
	    # 返回一个HttpResponse对象加括号调用HttpResponse的类
	    return HttpResponse(content, content_type, status)

	====================================================================================
	
	def redirect(to, *args, **kwargs):
		'传递要重定向的一个硬编码的URL或者路由'
	    if kwargs.pop('permanent', False):
	    '如果参数中包含 'permanent',并且其值为 True,则使用 HttpResponsePermanentRedirect 类'
	        redirect_class = HttpResponsePermanentRedirect
	    else:
	    	'否则使用默认的 HttpResponseRedirect 类'
	        redirect_class = HttpResponseRedirect
	        
		'''
		这里返回一个HttpResponse对象加括号调用HttpResponse的类
		创建相应的重定向对象,将重定向目标设置为 to
		'''
	    return redirect_class(resolve_url(to, *args, **kwargs))
	'''
		这样的设计使得 redirect 函数能够支持两种类型的重定向:
		临时重定向(HttpResponseRedirect)和永久重定向(HttpResponsePermanentRedirect),
		具体取决于 'permanent' 参数的值。
	'''

三板斧的使用

现在我们看了源码大概的注解,可以确定三种响应方式都是需要返回一个HttpResponse对象,下面就大概介绍一下大概的使用场景

HttpResponse

HttpResponse是一个通用的HTTP响应类,用于创建自定义的HTTP响应。适用于需要更细粒度控制响应内容、状态码和头部的情况。

示例:

	from django.shortcuts import HttpResponse
	
	def my_custom_response(request):
	    # 一些逻辑处理后,生成自定义的响应内容
	    content = 'This is a custom response.'
	    # 使用HttpResponse类创建HTTP响应
	    response = HttpResponse(content, content_type='text/plain', status=200)
	    # 可以添加自定义的头部信息
	    response['Custom-Header'] = 'Some value'
	    return response

Django之视图层_第3张图片

	from django.shortcuts import HttpResponse
	
	def index(request):
    response = HttpResponse()  # 实例化产生一个对象

    response.content = 'Lucky'  # 设置响应内容
    response.status_code = 404  # 设置响应状态码
    # 直接写出文本
    response.write('777777')
    # 一次性读取剩余字节,冲刷缓存区
    response.flush()
    return response

Django之视图层_第4张图片


redirect

redirect用于生成HTTP重定向响应,将用户从一个URL导航到另一个URL。适用于需要将用户引导到其他页面的情况。

示例:

	from django.shortcuts import redirect
	def my_redirect_view(request):
	    # 某些逻辑处理后,决定重定向到另一个URL
	    new_url = '/index/'
	
	    # 使用redirect函数生成重定向响应
	    return redirect(new_url)

Django之视图层_第5张图片


render

render主要用于渲染模板并将其与上下文一起返回,以生成HTML内容的HTTP响应。通常在视图函数中使用。

示例:

	from django.shortcuts import render

	def my_view(request):
	    # 从数据库获取数据或进行其他逻辑处理
	    data = {'foo': 'bar'}
	
	    # 使用render函数渲染模板,并将数据传递给模板
	    return render(request, 'my_template.html', {'data': data})

总结:

render用于呈现动态生成的HTML内容,redirect用于进行URL重定向,而HttpResponse用于构建更自定义的HTTP响应,适用于各种情况。在实际开发中,通常会根据具体需求选择使用这三者中的一种或多种。


JsonResponse对象

​ 前后端数据交互需要使用到json作为序列化,实现跨语言数据传输。

视图函数返回json格式的数据

  • 方法一
    json序列化形式
	import json
	def Myjson(request):
	    user_dict = {'name':'oscar','age':18,'hobby':['唱','跳','rap']}
	    '''如果数据中有中文会进行编码,如果不想它编码就直接设置ensure_ascii=False即可'''
    	data = json.dumps(user_dict,ensure_ascii=False)
	    return HttpResponse(data)

Django之视图层_第6张图片

  • 方法二
    JsonResponse序列化形式
	'需要先导入JsonResponse模块'
	from django.http import JsonResponse
	
	def js(request):
	    user_dict = {'name': 'oscar', 'age': 18, 'hobby': ['唱', '跳', 'rap']}
	    return JsonResponse(user_dict)

Django之视图层_第7张图片

可以看到和Json序列化一样的,都会默认把中文编码,但是我们不需要给中文编码,所以我们去看看JsonResponse的源码

Django之视图层_第8张图片

我们通过看源码可以知道,可以更改的东西很多,但是这里暂时就只修改一下默认的自动编码中文,和默认只允许通过字典的形式转换成为JSON格式

Django之视图层_第9张图片

在看源码中我们知道了可以通过这个json_dumps_params参数进行设置取消自动编码中文的代码,因为我们发送的数据都会被data接收,而data变量括号中,第二个参数cls就是类名,它并没有接收任何参数,而第三个参数就是我们需要的,而**json_dumps_params中的**是在在实参中起了作用,将字典打散成关键字参数,但是现在字典是空的,所以我们可以通过把json_dumps_params参数的None值更改成ensure_ascii=False就可以让它不走这个if判断,就会把我们设置的参数添加到字典当中,但是因为是字典的形式所以我们需要把它变成K:V键值对的形式表现出来

然后再视图函数这里,在返回值这里使用这个参数,然后使用字典的形式把json序列化的取消自动编码中文的代码写入

Django之视图层_第10张图片

Django之视图层_第11张图片

然后我们在JsonResponse源码中知道了safe参数就是设置只允许字典格式的数据才能转换成JSON格式,而我们只需要修改safe这个参数的值就可以了

Django之视图层_第12张图片
在这里插入图片描述
Django之视图层_第13张图片

最后可以看到我们是成功修改了,所以通过看源码我们能够学习到很多


form表单携带文件数据

我们在后台想接收文件数据,那么就会使用到form表单,已知接收数据form表单需要使用到post请求方式表单必须有属性 enctype="multipart/form-data"。那么我们就开始测试吧

注意事项:

  • enctype属性需要由默认的urlencoded变成form/data(enctype=“multipart/form-data”)

  • method属性需要由默认的get变成post
    (目前还需要考虑的是 提交post请求需要将配置文件中的csrf中间件注释)

  • 如果form表单上传文件 后端需要在request.FILES获取文件数据 而不再是POST里面

首先需要建立一个HTML页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="" method="post" enctype="multipart/form-data">
    	<input type="text" name="text" placeholder="请输入文字">
        <input type="file" name="myfile">
        <input type="submit" value="提交">
    </form>
</body>
</html>

然后再建立路由和视图函数

	'路由文件'
	from app import views
	urlpatterns = [
	    url(r'^func/', views.func),
	]

	'视图文件'
	def func(request):
	    print(request.POST)
	    return render(request,'index.html')

配置后这些后,直接访问对应的路由,然后点击选择文件后提交

Django之视图层_第14张图片

结果发现仅仅只是接收到了普通文本数据,并没有接收到文件数据
在这里插入图片描述

由此我们可以知道request.POST接收到的都是普通数据,并不能接收到文件数据

这时我们可以使用request.FILES来获取文件数据

	def func(request):
	    print(request.POST)
	    print(request.FILES)
	    return render(request,'index.html')

在这里插入图片描述

可以看到我们通过request.FILES方式可以获取到文件数据,但是却是不能获取到普通数据,所以可以说明request.FILES方法只能获取文件数据


然后我们可以通过对获取到的文件数据进行写入文件中

  • 首先我们从上述过程中,可以知道,得到的数据格式是字典格式,所以我们可以通过字典的方式取出数据
	def func(request):
	    if request.method == 'POST':
	        file_info= request.FILES.get('myfile')
	        print(res)
	    return render(request,'index.html')

在这里插入图片描述

  • 拿到名字后就可以通过写入文件的方式写入文件数据了,但是要注意需要使用wb模式写入,因为wb模式可以把二进制数据转换成str格式。

如果不是使用wb模式使用的是普通写模式(w)的话,会报write括号内只能接受str格式不能是二进制bytes格式
Django之视图层_第15张图片

	def func(request):
	    if request.method == 'POST':
	        # print(request.FILES)
	        file_info = request.FILES.get('myfile')
	        
	        with open(file_info.name,'wb')as f: 
	        '避免文件数据过大,使用一行一行的写入'
	            for line in file_info:
	                f.write(line)
	
	    return render(request,'index.html')

Django之视图层_第16张图片

此时就可以看到,文件已经写入进来了。


request对象方法

1.获取请求方式POST/GET
request.method
一个字符串,表示请求使用的HTTP 方法。必须使用大写。

2.request.POST
获取POST请求提交普通的键值对数据 一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成

3.获取GET请求
request.GET
获取GET请求	一个类似于字典的对象,包含 HTTP GET 的所有参数

4.获取文件
request.FILES
一个类似于字典的对象,包含所有的上传文件信息。
FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。
注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会包含数据。否则,FILES 将为一个空的类似于字典的对象。

5.原生的浏览器发过来的二进制数据
request.body  
一个字符串,代表请求报文的主体。在处理非 HTTP 形式的报文时非常有用,
  例如:二进制图片、XML,Json等。

6.拿到路由
request.path 
个字符串,表示请求的路径组件(不含域名)

7.拿到路由
request.path_info

8.能过获取完整的url及问号后面的参数 
request.get_full_path() 

FBV与CBV

FBV

FBV(Function Base Views)基于函数的视图;在视图里面通过函数来处理请求、响应请求。在之前Django学习中我们一直使用的都是这种方式。这里就不过多介绍了

CBV

CBV(Class Base Views) 基于类的视图;在视图里通过面向对象的方式来处理、响应请求

Python是一个面向对象的编程语言,如果只用函数来开发,很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View。可以让我们用类写View。这样做的优点主要下面两种:

  1. 提高代码的复用性,可以使用面向对象的技术。
  2. 根据不同的类方法处理不同的HTTP请求,而不是通过很多if判断,提高代码可读性。

如果要写一个处理GET请求的view,FBV是这样写的

	路由文件
	from app import views
	urlpatterns = [
    	url(r'^test/',views.test)
	]

	视图文件
	from django.shortcuts import HttpResponse
	def test(request):
	    if request.method == 'GET':
	        return HttpResponse('this is test')

使用CBV是这样写的

	路由文件
	from app import views
	urlpatterns = [
    	url(r'^Mytest/',views.My_test.as_view()),
	]

	视图文件
	from django.shortcuts import HttpResponse
	from django.views import View
	class My_test(View):
	    def get(self,request):
	        return HttpResponse('this is test')

CBV类中的不能随意的定义函数,只能使用八大请求方式

	'get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'
	'所有的类必须继承django的view类'
	from django.views import View
	
	class MyLogin(View):
	    # 类里面的方法名字不能够随便写,目前只能写get post等
	    # 访问这个地址必须是get请求方式
	    def get(self, request):
	        # get() takes 1 positional argument but 2 were given
	        print("get")
	        return HttpResponse("get")
	
	    # 访问这个方法必须是psot请求方式
	    # 通过form表单发送post请求
	    # 出了form表单,我们还可以使用工具来模拟
	    def post(self,request):
	        print("post")
	        return HttpResponse("post")

	postman的官网地址:https://www.postman.com/downloads/
	apizza的挂网地址:http://www.apizza.net/

从浏览器发送过来的请求会被Django根据URL传递给某个对应的函数,而不是class。针对这个问题,CBV提供了一个as_view()类方法,为何调用这个类方法就能让我们类里面的函数接收请求了呢?那我们就来看看它的源码


CBV的源码解析

Django之视图层_第17张图片

根据之前的操作就知道,我们在路由里面指向的函数不用我们手动调用,Django会自动调用并传递一个请求过去,那么上面也看到了,这个方法是我们手动调用的,这是因为我们需要让Django调用的是view函数,并且把请求给这个函数。

	urlpatterns = [
	    url('^index/', views.index.as_view())
	    # 手动调用后会拿到view方法的返回值。变成了以下的样子
	    url('^index/', views.index.view)
	    # 那么此时Django会帮助我们调用一次这个方法,并且传递一个request请求过去。
	]

view方法解析
那么我们再看看view方法干了些什么事情:只看红框内容即可
Django之视图层_第18张图片

  • 首先是根据cls实例化出一个对象了(这cls就是我们在视图内定义的类)
  • 将request请求给这个对象作为属性
  • 而args、kwargs则是防止有名分组或无名分组,用于接收分组产生的值。也一同赋给这个对象。
  • 调用这个对象下面的dispatch方法,并将Django传递的参数一并传递过去了

dispatch方法解析
再分析一下dispatch方法的作用:只关注红框内容即可
Django之视图层_第19张图片

request.method.lower()将请求的方法变成小写的字符串

主要就是判断:发送请求的方法是否符合正常HTTP请求

	http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

如果符合的话:使用反射通过拿到的字符串去对象里查找相同名称的函数。比如:

	class index(View):
		def get(self,request):
			return HttpResponse('from GET')

	self = index类实例化的对象,并且拥有request、args、kwargs等属性
	# 等同于:如果request.method.Lower()是get的话,拿到get方法的函数对象
	handle = getattr(self,'get',None) # 地三个参数则是没有找到get方法或属性返回的

	handle = get函数对象

	handle(request,*args,**kwargs) # 等同于:get(self,request,*args,**kwargs)

调用时将request请求、一些额外参数都传递给了我们定义的类里面的get方法

演示:不定义人任何处理请求的函数

	class index(View):
    	pass

Django之视图层_第20张图片
浏览器得不到Django响应的任何数据!

get方法需要返回一个HttpResponse对象,没有返回的话,则页面报错

	class index(View):
		def get(self,request):
			print('......')

Django之视图层_第21张图片
注意:如果类里面没有定义处理对应请求的函数,浏览器得不到响应,专门处理某个请求的函数名一定要与请求名一致。

总结

我们首先得知道使用CBV目的是什么,又或者Django引入CBV目的是什么。其实在早期版本使用Django普遍使用的都是FBV编写模式,甚至认为FBV比CBV更好用,实则不然。Python中一大特性便是面向对象,CBV是通过面向对象来实现视图的,而它相对于函数而言,具备了多种特性。而利用多态性更容易从宏观层面上将项目内的比较通用的功能抽象出来。

而CBV的实现通过上述源码的分析也可以看出来,大致就是通过URL执行类里面的view方法,再通过其内部的dispatch方法进行分发处理,将浏览器请求交给我们的类里面对应的方法进行处理。

你可能感兴趣的:(Django,django)