DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)

文章目录

  • DRF入门规范
  • 一、Web应用模式
    • 1.前后端混合开发模式
    • 2.前后端分离开发模式
  • 二、API接口
    • 概念
    • Json格式与XML格式的区别
    • 前端、前台、后端以及后台的区别
  • 三、接口测试工具:`Postman/Apifox`
  • 四、Restful规范
    • 概念
    • Restful 10条规范
  • 五、序列化和反序列化
    • 序列化
    • 反序列化
  • 六、基于原生Django框架编写5个接口
  • 七、DjangoRestFramework
    • DRF的下载与使用
    • 下载
    • 使用
  • 八、CBV源码分析
  • 九、APIView的使用
    • 继承APIView+Response写五个接口
  • 十、APIView源码分析
    • 总结

DRF入门规范

DRF即Django REST Framework的缩写,官网上说:Django REST framework是一个强大而灵活的工具包,用于构建Web API。
简单来说:通过DRF创建API后,就可以通过HTTP请求来获取、创建、更新或者删除数据(CRUD)

DRF(django rest framework)是基于django的一个框架,可以帮助我们快速开发restful规范的的接口框架https://www.django-rest-framework.org/

一、Web应用模式

在开发Web应用中,有两种应用模式:

1.前后端混合开发模式

前后端混合项目是指前后端代码的混合,前端需要使用模版语法去获取后端的数据,后端需要使用locals在开放局部空间来让前端拿到数据。

  • 模版语法简称Dtl:Django template language
  • 模版渲染:在后端完成

大体流程就是:前端在编写完静态页面之后,需要发送给后端人员做二次处理,所有的html代码和数据在服务器拼接好,等前后端处理好模版之后在做整合,然后一次性将所有内容发送到客户端,浏览器执行代码,将内容呈现给客户。

  • 缺点:前后端交接困难,对彼此代码不熟悉;代码会产生覆盖,工作量大
  • 例如:标签没有闭合二导致页面混乱,JavaScript位置不对导致效果报错,前端人员需要到服务端代码修改。从公共服务器下载,修改后在传送到公共服务器中,服务端人员需要更新到本地计算机中才能向公共服务器中上传文件,否则就会覆盖

DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第1张图片

2.前后端分离开发模式

前后端分离开发模式是指代码上的分离,前后端代码上面的数据传输和交互使用API接口的方式使用Json格式的数据进行交互,脱离了模版语法。也就是说前后端与浏览器的交互是分开的把界面交互和数据交互分开(这里的数据交互必须是Json格式或者XML,现在XML基本很少了),后端人员向前端开放数据接口(路由),视图层不在直接返回给浏览器了,而是与前端做起了数据交互,实际上返回界面的还是前端,前端工作人员调用接口获取数据,从而将数据显示到页面

  • 后端就做后端的事:开发API接口以及跟前端进行交互
  • 前端就做前端的事:使用Ajax和后端交互

大体流程:浏览器发送请求,这时候前端返回一个静态页面,当静态页面中触发js事件的时候,通过Ajax向服务器端发送请求,索要数据,然后将数据拿到之后再填充到浏览器的页面中。

  • 优点:职责分工明确,独立开发,互不影响。前端人员负责数据展示,服务器段人员负责业务逻辑编写。界面出现问题则找前端,逻辑问题找服务器端。

DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第2张图片

而当需要数据测试的时候,前端可以使用Mock平台来模拟接口数据,而后端可以使用软件Apifox或者Postman模拟前端发送请求。


二、API接口

概念

API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件的以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。API除了有应用“应用程序接口”的意思外,还特指 API的说明文档,也称为帮助文档。

API:应用程序接口(API:Application Program Interface)是一组定义、程序及协议的集合,通过 API 接口实现计算机软件之间的相互通信。API 的一个主要功能是提供通用功能集。程序员通过调用 API 函数对应用程序进行开发,可以减轻编程任务。 API 同时也是一种中间件,为各种不同平台提供数据共享。

简单理解API:
为了在团队内部形成共识、防止因个人习惯差异引起的混乱,我们需要找到并形成团队共识、统一的接口规范,而且这种规范能够让后端写的接口,用途一目了然,减少双方之间的合作成本。

通过网络,规定了前后台信息交互规则的url链接,也就是前后台信息交互的媒介

	http://127.0.0.1:8000/login/  这就是API接口

Web API接口和一般的url链接还是有区别的,Web API接口简单概括有四大特点:

	1.url地址:长得像返回数据的url链接
		https://api.map.baidu.com/place/v2/search
		
	2.请求方式:get、post、put、patch、delete
		采用get方式请求上方接口
		
	3.请求参数:Json格式或者XML格式的key-value类型数据
	'请求数据:地址栏中的数据----->request.GET,请求体中数据:reqeust.POST'
		早些年	前后端交互使用XML格式,ajax其实就是异步JavaScript和XML
		后来	随着Json格式的出现,乃至今日都是主流
		未来	可能会有更高效、安全的交互格式会替代目前的Json格式
	
	4.响应结果:Json格式或者XML格式的数据

Json格式与XML格式的区别

  1. JSONJavaScript Object NotationXML是可扩展标记语言
  2. JSON是基于JavaScript语言;XML源自于SGML
  3. JSON是一种表示对象的方式;XML是一种标记语言,使用标记结构来表示数据项。
  4. JSON不提供对命名空间的任何支持;XML支持名称空间
  5. JSON支持数组;XML不支持数组
  6. XML的文件相对难以阅读和解释;而JSON相比较XML的文件非常易于阅读
  7. JSON支持结束标记;XML有开始和结束标签
  8. JSON的安全性较低;XMLJSON更安全
  9. JSON不支持注释;XML支持注释
  10. JSON仅支持UTF-8编码XML支持各种编码

前端、前台、后端以及后台的区别

前后端是指代码上的,前后台是指用户视觉上的。

DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第3张图片

前端指的是大前端,不仅仅是网站,还有app、小程序。而后端指的是Python、Golang、Java等的Web后端


三、接口测试工具:Postman/Apifox

Postman是一款接口调试工具,是一款免费的可视化软件,同时支持各种操作系统平台。主要功能是发送http请求。

Postman可以直接去官网地址下载:https://www.postman.com/downloads/,然后傻瓜式安装即可


Apifox也是一款接口调试工具,也是免费的可视化软件,相较而言我觉得是比Postman更好用一下。
Apifox = Postman + Swagger(接口文档) + Mock(假数据) + JMeter(压测工具)

Apifox也是直接去官网地址下载:https://apifox.com/,下载后安装选择自己想要安装的位置即可

因为这两款软件操作都差不多,我这里就使用一下Apifox软件来展示吧

  1. 创建团队
    DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第4张图片

  2. 创建项目
    DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第5张图片

  3. 新建接口
    DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第6张图片

  4. 大致操作
    DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第7张图片

  5. 请求体
    DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第8张图片

  6. 请求头
    DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第9张图片

也可以看一下Postman的吧,Postman也是需要注册才可以使用,而Apifox只需要微信扫码即可
但是Postman无需创建团队以及创建项目即可使用,如下
DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第10张图片

当然市面上还有很多接口测试软件,有些是软件的也有些是网页的,具体看个人喜好选择。
Postman、Eolink、SoapUI、JMeter、REST-Assured、apizza、Postwoman等等这些。


四、Restful规范

概念

REST全称是Representational State Transfer中文意思是表述(表征行状态转移),它首次出现在2000年Roy fielding的博士论文中。是一种Web服务API接口的设计风格,主要适用于前后端分离的应用模式中。

这种风格的理念认为后端开发任务就是提供数据的,对外提供的事数据资源的访问接口,所以在定义接口时,客户端访问的URL路径就表示这种要操作的数据资源,事实上我们可以使用任何一个框架都可以实现符合restful规范的api接口

Restful 10条规范

  • 数据的安全保障,通常使用https(http+ssl/tsl)协议
    • url链接一般都采用https协议进行传输
    • 采用https协议,可以提供数据交互过程中的安全性
  • 接口特征表现:在API地址中带接口标识,一般放在地址栏中(放在域名中)
	https://api.baidu.com	(这种就是放在域名中)
	https://baidu.com/api	
	'''看到API关键字,就代表该请求URL链接是完成齐纳后端数据交互的'''
  • 多版本共存:在URL链接中带版本标识
	https://api.weibo.com/2/
	https://api.weibo.com/v2/
	https://api.weibo.com/?version=2
	https://api.weibo.com/v1/login  ---->如需要的参数为name和password
	https://api.weibo.com/v2/login  ---->如需要的参数为name和password和code
	'''
	URL链接中的版本就是不同数据版本的体现,如一个app新老版本不同的接口,不能合并直接在一个接口上迭代,需要重构一个接口用于
	新版本使用的,而旧接口则给老版本使用。毕竟用户可能不太想更新,如果直接迭代升级可能添加了东西需要多余的参数才能使用
	'''
  • 数据即是资源,均使用名词(可复数),
    • 接口一般都是完成前后端数据的交互,而交互的的数据我们称之为资源
    	https://api.baidu.com/v1/users
    	https://api.baidu.com/books
    
    • 资源名一般提倡用名词复数形式,在URL链接中尽量避免出现动词资源名
    	'错误示范'
    	https://api.baidu.com/v1/get_users	
    	'特殊的接口可以出现动词,因为这些接口没有一个明确的资源,或是动词就是接口的核心含义'
    	https://api.baidu.com/login
    
  • 资源操作由请求方式决定(method)
    • 操作资源一般都会涉及到增删改查,我们提供请求方式来标识增删改查动作
    	https://api.baidu.com/books		get请求:获取所有图书
    	https://api.baidu.com/books/1	get请求:获取主键为1的图书
    	https://api.baidu.com/books		post请求:新增一本图书
    	https://api.baidu.com/books/1	put请求:整体修改主键为1的图书
    	https://api.baidu.com/books/1	patch请求:局部修改主键为1的图书
    	https://api.baidu.com/books/1	delete请求:删除主键为1的图书
    
  • 在请求地址中带过滤条件
	'通过在url上传参的形式传递搜索条件'
	https://api.baidu.com/v1/zoos?name=猴子
  • 响应状态码(两套)

    • http响应状态码
    	响应状态码1xx		请求正在处理
    	响应状态码2xx		200:常规请求	201:创建成功
    	响应状态码3xx		301:永久重定向	302:暂时重定向
    	响应状态码4xx		403:请求无权限	404:请求路径不存在
    	响应状态码5xx		500:服务器异常
    
    • 自己自定的状态码(使用频率高),主要看公司内部规定,放在响应体中
    	如:
    	错误代码	错误信息	详细描述
        10001	System error	系统错误
        10002	Service unavailable	服务暂停
        10003	Remote service error	远程服务错误
    
  • 返回数据中带错误信息

	{'code':100,'Message':'send success'}
  • 返回结果 针对不同操作,服务器向用户返回的结果应该符合以下规范
	GET 获取所有数据	/collection	返回资源对象的列表(数组)[{'name':'西游记','price':66},{'name':'东游记','price':77}]
	GET	单个对象	/collection/resource    返回单个资源对象	{'name':'西游记','price':66}
	POST 新增对象	/collection	  返回新生成的资源对象        {'name':'新西游记','price':77}
    PUT  修改对象	/collection/resource	返回完整的资源对象  {'name':'新西游记','price':77}
    PATCH 修改对象 	/collection/resource	返回完整的资源对象  {'name':'新西游记','price':77}
    DELETE 删除 	/collection/resource	返回一个空文档        
  • 需要url请求的资源需要访问资源的请求链接
	"url": "http://blog.sina.com.cn/zaku",

五、序列化和反序列化

api接口开发,最核心最常见的一个过程就是序列化,所谓序列化就是把[数据转换格式],序列化可以分为两个阶段

序列化

把我们能识别的数据结构(python的字典,列表,对象)转换成其他语言(程序)能识别的数据结构

	python的字典,列表,对象--------》转换成json格式字符串(可以是别的格式)
	前后端交互,目前通常使用,json格式字符串交互
	前后端分离模式中,主要指:
		'''
		前端发送请求获取数据,后端去数据库中查询,因为查出来的是模型对象(QuerySet对象),
		但是模型对象数据无法直接提供给前端或者别的平台使用,所以需要把数据进行序列化后变成字符串或者json数据,
		然后返回给前端的这个过程,称之为序列化
		'''

反序列化

把其它程序(语言)提供的数据转换成(还原成)我们能够识别的数据结构

	前端给我们的json格式数据-------》转换成字典,列表,对象
	前后端分离模式中,主要指的是:
		'''
		前端携带json格式数据到后端,对应python而言就是字符串,需要进行反序列化成模型类对象,
		然后通过ORM操作保存到数据库中的这个过程,称之为反序列化
		'''

六、基于原生Django框架编写5个接口

'以后写的接口,基本都是5个接口及其变形'
   查询所有:JsonResponse   HttpResponse
   查询单个:根据pk查到单个对象,直接序列化后返回给前端
   新增一个:request.POST 取出前端传入body的数据,只能取出form-data,urlencoded,不能取出josn格式,request.body  --->json--->反序列化成字典,再用(name=lqz&age=19)
   修改一个:put请求放到body中的数据,在request.POST取不出来,只能自己从request.body中取出来
   删除一个:通过pk直接删除
'基于books单表为例,写5个接口'
   创建book表
   表迁移
   录入表的数据:直接录入/后台管理录入
   写查询所有接口   遵循restful规范,使用cbv
   新增一个数据 post()
   查询一个数据 get()
   修改一个数据:put提交的数据,不能从requets.POST中提取,>>>request.body
   删除一个数据 delete()

1.模型层urls.py

	from django.db import models
	class Book(models.Model):
	    name = models.CharField(max_length=32)
	    price = models.IntegerField()
	'''这里我就没去settings中修改默认数据库了,直接使用自带的sqlite3演示'''
	记得创建好需要执行迁移命令:
		python38 manage.py makemigrations
		python38 manage.py migrate

2.视图层views.py

	'''
	注意:写查询所有接口 遵循restful规范,然后使用cbv的方式写
	注意用到提交数据,修改数据的情况,需要注释crsf第四个中间件,否则过不了csrf认证
	'''
	from django.views import View
	from app01 import models
	from django.http import JsonResponse
	import json
	class BookView(View):
	    def get(self, request):
	        book_list = models.Book.objects.all()
	        l = []
	        for book in book_list:
	            l.append({'name': book.name, 'price': book.price})
	        return JsonResponse({'code': 100, 'msg': '查询成功', 'results': l})
		
	    def post(self,request):
	    '''
		新增(可以使用urlencoded或form-data编码,使用json形式编码也行,
		使用urlencode或form-data编码,就得从request.POST中去取,一个个取
		使用json形式编码,json格式编码提交的数据,需要反序列化后,从request.body中取)
		'''
	        data = json.loads(request.body)
	        print(data)
	        book = models.Book.objects.create(**data)
	        return JsonResponse({'code': 100, 'msg': '添加成功','results': {'name': book.name, 'price': book.price},},json_dumps_params={'ensure_ascii':False})
	
	
	class BookDetailView(View):
	    def get(self, request, pk):
	        book_obj = models.Book.objects.filter(pk=pk).first()
	        l = []
	        l.append({'name': book_obj.name, 'price': book_obj.price})
	        return JsonResponse({'code': 100, 'msg': '查询一条成功', 'results': l})
	
	    def put(self, request, pk):
	        book_obj = models.Book.objects.filter(pk=pk).first()
	        if book_obj:
	            data = json.loads(request.body)
	            print(data)
	            models.Book.objects.filter(pk=pk).update(**data)
	            return JsonResponse({'code': 100, 'msg': '修改一条成功'})
	        else:
	            return JsonResponse({'code': 101, 'msg': '当前不存在此条数据'})
	
	    def delete(self,request,pk):
	        book_obj = models.Book.objects.filter(pk=pk).exists()
	        if book_obj:
	            models.Book.objects.filter(pk=pk).delete()
	            return JsonResponse({'code': 100, 'msg': '删除一条成功'})
	        else:
	            return JsonResponse({'code': 101, 'msg': '当前不存在此条数据'})

3.路由层urls.py

	from django.contrib import admin
	from django.urls import path
	from app01 import views
	urlpatterns = [
	    path('admin/', admin.site.urls),
	    path('books/', views.BookView.as_view()),
	    '''路由不一样,需要新写一个路由,可用转换器或者正则表达式,但使用转换器较多'''
	    path('books/', views.BookDetailView.as_view()),
	]

七、DjangoRestFramework

核心思想: 缩减编写api接口的代码

Django REST Framework是一个建立在Django基础之上的Web应用开发框架 可以快速的开发REST API接口应用
在DjangoRestFramework中提供了序列化器Serialzier的定义 可以帮助我们简化序列化与反序列化的过程
还提供丰富的类视图、扩展类、视图集来简化视图的编写工作DjangoRestFramework还提供了认证、权限、限流、过滤、分页、接口文档等功能的支持 DjangoRestFramework提供了一个API 的Web可视化界面来方便查看测试接口

我们在写原生的前后端分离项目是使用的是Django的Web框架(只写接口 使用JsonResponse返回即可)
但是想让前后端分离更方便就可以用到DjangoRestFramework可以方便我们快速写出符合Restful规范的接口

DRF的下载与使用

官方文档:Home - Django REST framework

下载

	pip38 install djangorestframework

注意:如果下载的Django版本过低(2.x)的话,下载drf会发现Django版本太低不支持,然后会自动给你卸载重装最新版的Django,但是如果Django的版本不低(3.x),下载drf就不会有任何影响

使用

首先需要settings.py中的INSTALLED_APPS添加rest_framework应用

	INSTALLED_APPS = [
	    'rest_framework',
	]

注意:如果不注册应用,在浏览器中就访问不了。注册了就可以看到一个页面展示。当然不注册也可以使用api测试工具来访问

没注册直接使用浏览器访问的效果

DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第11张图片

注册应用后使用浏览器访问的效果DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第12张图片
没注册使用接口测试工具访问的效果
DRF从入门到精通一(DRF介绍、API接口、接口测试工具、Restful规范、序列化反序列化、基于drf、原生编写接口,APIView源码分析)_第13张图片

下面我用一个基于drf编写5个接口

1.先在models.py中创建一些字段

	class Book(models.Model):
	    name = models.CharField(max_length=32)
	    price = models.IntegerField()
	'''这里我就没去settings中修改默认数据库了,直接使用自带的sqlite3演示'''
	记得创建好需要执行迁移命令:
		python38 manage.py makemigrations
		python38 manage.py migrate

2.创建序列化类

在应用下面创建一个serializers.py文件

	from rest_framework import serializers
	from .models import Book
	class BookSerializer(serializers.ModelSerializer):
	    class Meta:
	        model = Book
	        fields = '__all__'

序列化类(Serializer)将Model转换为序列化的JSON对象。
上面构建了BookSerializer的序列化类。序列化类看起来和django的Form很像。

3.创建视图views.py

	from .serializer import BookSerializer
	from rest_framework.viewsets import ModelViewSet
	from app01 import models
	class DrfBookView(ModelViewSet):
	    queryset = models.Book.objects.all()
	    serializer_class = BookSerializer

我们使用了ModelViewSet来快速构建一组view。由于背后实现了很多方法,这看起来有些像魔法。

4.配置路由urls.py

	from django.urls import path
	from app01 import views
	from rest_framework.routers import SimpleRouter
	router = SimpleRouter()
	router.register('books',views.DrfBookView,'books')
	urlpatterns = [
	    path('admin/', admin.site.urls),
	]
	urlpatterns += router.urls

因为我们使用的是ViewSet而不是view,所以我们可以通过简单地将ViewSet注册到Router来自动生成API的 URL conf。

可以发现,和Django程序相比,我们没有写template做前端的显示,而是写了序列化类提供API。前端可以分离出来,使用API来和后端通信。


八、CBV源码分析

	'cbv写好后,路由配置如下'
	-第一个参数是路径,第二个参数是试图函数的内存地址(视图类执行as_view这个类方法,把它执行完,
	结果放在第二个参数上:我们猜执行完的结果是个函数内存地址)
	 path('books/', views.BookView.as_view()),
    
    -去找as_view,去BookView类中找,找不到没有,则去父类中找View
        @classonlymethod
        def as_view(cls, **initkwargs):
            def view(request, *args, **kwargs):
	            self = cls(**initkwargs)  # BookView类实例化得到对象
                return self.dispatch(request, *args, **kwargs) # BookView类的dispatch方法
                '''如果BookView类的dispatch没有,则会去父类View中查找dispatch'''
            return view
    -当请求来了,路由匹配成功,会执行view(request)--->本质执行self.dispatch(request, *args, **kwargs)
    -去View中找到了dispatch
        def dispatch(self, request, *args, **kwargs):
            # 请求方式转成小写,假设 get 请求,符合if条件
            if request.method.lower() in self.http_method_names:
                # 反射 getattr(对象,'字符串','默认值')
                # self是BookView的对象
                # handler 就是BookView类的get方法
                handler = getattr(self, 'get', self.http_method_not_allowed)
            else:
                handler = self.http_method_not_allowed
            return handler(request, *args, **kwargs)  # get(request)
        '''
		小结:在View中找到dispatch后,会根据请求方式,
		然后通过反射去视图类中的[BookView]反射出跟请求方式同名的方法,然后执行
		'''

九、APIView的使用

继承APIView+Response写五个接口

五个接口为:对数据库的增删改查

APIView是基于Django原生的View编写接口的,是drf提供的一个类(APIView),使用drf编写视图类,都是继承这个类及子类,APIView本身就是继承了Django原生的View

1.视图Views.py

	from rest_framework.views import APIView
	from rest_framework.response import Response
	from app01 import models
	'drf的视图类比较规范,导入都有单独的模块'

	class BookView(APIView):
	    def get(self, request):
	        book_list = models.Book.objects.all()
	        l = []
	        for book in book_list:
	            l.append({'name': book.name, 'price': book.price})
	        return Response({'code': 100, 'msg': '查询成功', 'results': l})
	
	    def post(self,request):
	        data = json.loads(request.body)
	       	models.Book.objects.create(name=request.data.get('name'), price=request.data.get('price'))
	        return Response({'code': 100, 'msg': '添加成功','results': request.data})
	
	
	class BookDetailView(APIView):
	    def get(self, request, pk):
	        book_obj = models.Book.objects.filter(pk=pk).first()
	        l = []
	        l.append({'name': book_obj.name, 'price': book_obj.price})
	        return Response({'code': 100, 'msg': '查询一条成功', 'results': l})
	
	    def put(self, request, pk):
	        book_obj = models.Book.objects.filter(pk=pk).first()
	        if book_obj:
	            book_obj.name = request.data.get('name')
	            book_obj.price = request.data.get('price')
	            book_obj.save()
	            return Response({'code': 100, 'msg': '修改一条成功','results':request.data})
	        else:
	            return Response({'code': 101, 'msg': '当前不存在此条数据'})
	
	    def delete(self,request,pk):
	        book_obj = models.Book.objects.filter(pk=pk).exists()
	        if book_obj:
	            models.Book.objects.filter(pk=pk).delete()
	            return Response({'code': 100, 'msg': '删除一条成功'})
	        else:
	            return Response({'code': 101, 'msg': '当前不存在此条数据'})

2.路由urls.py

	from django.contrib import admin
	from django.urls import path
	from app01 import views
	urlpatterns = [
	    path('admin/', admin.site.urls),
	    path('books/', views.BookView.as_view()),
	    path('books/', views.BookDetailView.as_view()),
	]
	从上述代码中可以看到继承APIView+Response和Django原生View区别到底在哪?
		1.首先记住APIView类是drf提供的一个类,而drf是一个app,在django中三方模块都是一个个应用,
			它注册app,rest_framework,就可以在浏览器中直接访问,drf自带的页面查看数据等。
			而原生则无法查看,会直接报错,但是可以通过接口测试工具访问,但是仅只有纯json数据
		2.在进行post请求的时候,原生View需要注释掉csrf中间件,而APIView无需注释
		3.原生View传递数据时,如果是中文,需要声明ensure_ascii为False,而APIView无需
		4.原生view使用form-data/urlencoded用获取json格式的不一样,需要从request.body中按照所需切割,比较麻烦,
		  而APIView内部对数据进行了封装,直接使用request.data可自行判断是否是那种编码格式然后进行自行转换好,无需我们
		  进行操作

十、APIView源码分析

首先drf是一个Django第三方的App 它只适用于Django 如果使用到别的框架是使用不了的
安装drf之后就可以导入一个视图累APIView 所以要使用drf写视图类 都要继承APIView以及其子类

这里仅解读几个重要的源码

	首先在路由中:path('books/', views.BookView.as_view()),当请求来了
	然后会执行as_view(),因为是继承着APIView所以会进入到APIView中执行它的as_view,
	而APIView的as_view其实执行结果和Django原生View的as_view是一样的,不过去除了csrf认证
	class APIView(View):
		@classmethod
	    def as_view(cls, **initkwargs):
	    	'''
	    	如果视图类有queryset属性,且其类型为models.query.QuerySet,则设置一个函数force_evaluation
	    	这个函数用于防止直接评估queryset,因为结果会被缓存并在多个请求之间重复使用
	    	'''
	        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
	            def force_evaluation():
	                raise RuntimeError(
	                    'Do not evaluate the `.queryset` attribute directly, '
	                    'as the result will be cached and reused between requests. '
	                    'Use `.all()` or call `.get_queryset()` instead.'
	                )
	            cls.queryset._fetch_all = force_evaluation
			'''
			这里调用了父类的as_view方法,父类是Django原生的View
			然后把自己的参数传递了进去,并返回被csrf_exempt装饰的视图,这个csrf会把所有的请求csrf检验认证去掉
			'''
	        view = super().as_view(**initkwargs)
	        view.cls = cls
	        view.initkwargs = initkwargs
	
	        '''
			csrf_exempt是一个装饰器,用于豁免csrf验证
			相当于在所有的方法上加上了这个装饰器
			'''
	        return csrf_exempt(view)

			'''
			总结:当路由匹配成功后,会执行APIView父类的as_view,并且会先执行csrf_exempt(View)(request)
			然后按照正常执行Django原生的as_view方法,然后会执行它的view(request),最终会返回一个
			return self.dispatch(request, *args, **kwargs),它会从视图类中寻找,发现没有,
			因为视图类继承的是APIView,所以会优先从APIview中寻找是否有dispatch这个方法,结果找到了
			'''

		'dispatch方法用于处理传入的请求'
		def dispatch(self, request, *args, **kwargs):
			'保存参数'
	        self.args = args
	        self.kwargs = kwargs
			
	        request = self.initialize_request(request, *args, **kwargs)
	        '''
			这里把原来的request传入到self.initialize_request对象中
			然后会执行def initialize_request方法,最后它返回了一个Response(reqeust)
			它又把这个原来的request传入进去了,然后实例化对象,然后对象加括号会调用Response的初始化方法
			下面就是它的__init__方法,我们也对其进行解读
		  	def __init__(self, request, parsers=None, authenticators=None,
                negotiator=None, parser_context=None):
				这里它把传入过来的原request放到这个self._request中,这里的self已经不是视图类了,
				因为这个Request类没有继承任何一个类,它就是它自己,所以这个self是Request
		        self._request = request
		        # 指定解析器、身份验证器、内容协商器的参数,默认为 None 或空元组
		        self.parsers = parsers or ()
		        self.authenticators = authenticators or ()
		        self.negotiator = negotiator or self._default_negotiator()
		        self.parser_context = parser_context
		        # 用于保存请求数据的属性,初始值为 Empty(可能是一个自定义的占位符类)
		        self._data = Empty
		        self._files = Empty
		        self._full_data = Empty
		        self._content_type = Empty
		        self._stream = Empty
			'''
			'''
			从上面一路追源码可以看到,此时这里的self.request已经不是最开始的request了,变成了DRF提供的Request类的对象了
			而是initialize_request经过一系列操作得到的新的request了,然后此处的self则还是视图类,
			所以后续视图类的方法中,可以直接使用self.request取出它,视图类的request=新的request了
			'''
	        self.request = request
			
	        try:
	        	'执行initial方法,这里执行了三大认证,认证/频率/权限'
	            self.initial(request, *args, **kwargs)
				'通过反射方法,去视图类中执行跟请求方式同名的方法'
				# 这个就跟原来的那个View类的dispatch 判断当前是否拥有八大方法如果有则执行
	            if request.method.lower() in self.http_method_names:
	                handler = getattr(self, request.method.lower(),
	                                  self.http_method_not_allowed)
	            else:
	                handler = self.http_method_not_allowed
	            '因为这里得到同名的方法后传入的也是新的Request类对象了,那么视图类方法的传入的request也是新的request'
	            response = handler(request, *args, **kwargs)
	
	        except Exception as exc:
	        	'调用处理方法,如果执行三大认证或视图类方法中出现了错误,都会捕获异常,统一处理'
	            response = self.handle_exception(exc)
			'最终处理响应'
	        self.response = self.finalize_response(request, response, *args, **kwargs)
	        return self.response

总结

	只要继承APIView,以后方法中的request都变成rest_framework.request.Request的对象了(可以使用type方法查看一下)
		但是跟Django原生的用起来是一模一样的,仅只是添加新的功能了,相当于装饰器一样
	原生的Django request使用type方法是:django.core.handlers.wsgi.WSGIRequest类的对象
	
	'''
      总结:APIView的执行流程:
    	1 只要继承APIView都没有csrf的认证了
        2 以后视图类方法中得request对象,变成了新的request,
          它是rest_framework.request.Request 的对象了,但是用起来跟之前一样
        3.把新的request对象,同时放到了 视图类的对象中  self.request = request  
          后续从视图类中可以直接通过 self.request取出来
        4 执行视图类的方法之前,执行了3大认证(认证,权限,频率)
        5 如果在执行三大认证和视图类的方法过程中只要报错,都会被捕获处理(异常捕获是全局异常捕获)
    '''

	'在执行视图类的方法之前干了一些事'
	-去除csrf
    -包装新的requet
    -在视图类中加入了 self.request
    -执行三大认证
	'在执行视图类的方法之后干了一些事'
	-处理全局异常

你可能感兴趣的:(DRF,python)