发现会写接口还不太行,源码看了有点懵逼,决定好好缕一缕源码,茅塞顿开啊! 
可以更方便的使用django写出符合restful规范的接口
下载安装
pip3 install djangorestframework注意
rest_framework是一个app需要注册!??官网:https://www.django-rest-framework.org/

drf安装默认安装的最新版本,如果django版本过低会自动升级到3.x版本
版本支持对应关系

示例
'''views.py'''from rest_framework.views import APIViewfrom rest_framework.response import Responseclass Test(APIView): def get(self,request): data = {'status':200,'msg':'success'} return Response(data) def post(self,request): data = {'status': 200, 'msg': 'success'} return Response(data) '''urls.py''' path('test/', views.Test.as_view()), '''settings.py'''INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', 'rest_framework']get请求

post请求

注意:前后端分离csrf已经没有用了,查看一下源码
def as_view(cls, **initkwargs) return csrf_exempt(view)我们发现返回值是csrf_exempt(view)局部不验证,在Django总结到了不懂可以看一下:CSRF 跨站请求伪造
API接口开发,最核心最常见的一个过程就是序列化,所谓序列化就是把数据转换格式,序列化可以分两个阶段:
在Djangorestframework中的序列化反序列化又是如何?
如何使用DRF快速写出增删查改的接口?
快速写5个接口
使用Django写五个接口得配5个路由,5个视图函数去处理,现在使用drf不需要了,如下:
-查询所有---》get->http://127.0.0.1:8000/books/-查询一个---》get->http://127.0.0.1:8000/books/1/-新增一个---》post->http://127.0.0.1:8000/books/ #在body中带数据-修改-----》put,patch--->实际编码中,基本都用put http://127.0.0.1:8000/books/1/ body体中传入修改的数据-删除一个---》delete-->http://127.0.0.1:8000/books/1/ '''接口都是这五个的变形'''登录就是查询一个,注册就是新增一个'''在我们使用postman的时候,地址严格写,不能缺少/'''from rest_framework.viewsets import ModelViewSetfrom app import modelsfrom app.serializer import BookSerializerclass BookView(ModelViewSet): serializer_class =BookSerializer queryset = models.Book.objects.all()from rest_framework.serializers import ModelSerializerfrom app import modelsclass BookSerializer(ModelSerializer): class Meta: model = models.Book fields = '__all__'from rest_framework.routers import SimpleRouterfrom app import viewsrouter = SimpleRouter()router.register('books', views.BookView)'''register(prefix, viewset, base_name)prefix 该视图集的路由前缀viewset 视图集base_name 路由名称的前缀'''urlpatterns = [ path('admin/', admin.site.urls),]urlpatterns += router.urls# 千万注意别把注释写到urlpatterns列表中,那样就不是注释了,成字符串了!!!INSTALLED_APPS = [ 'rest_framework']from django.db import modelsclass Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=5,decimal_places=2) authors = models.CharField(max_length=32)获取所有

获取一条

新增一条数据

全部修改:修改id为1的数据,这里尽量在body体里写全参数

局部修改:修改id为1的数据,修改啥写啥

删除一条数据:删除id为2的数据


因为DRF框架里大部分都是基于CBV(视图类)写,所以流程是什么,如何执行需要了解,同时也方便理解APIView,顺便提一嘴APIView也是继承了View ---->
class APIView(View)这里需要强调一下,CBV路由归根结底还是FBV都是函数的内存地址,比如
views.类.as_view()底层仍然是函数的内存地址
CBV源码执行流程
'''views.py'''from django.views import Viewfrom django.http import HttpResponseclass TestView(View): def get(self,request): return HttpResponse('get请求') # post csrf认证注释掉就好了 def post(self,request): return HttpResponse('post请求')'''urls.py'''path('test/',views.TestView.as_view())写一个视图类,写了get方法和post方法,来了get请求就走get方法,来了post请求就走post方法,过程如何?且看分析源码执行过程~
'''请求来了在不考虑中间件的情况下,从路由的匹配关系和视图函数来看'''1、cbv路由写法:path('test/', views.TestView.as_view())# 第二个参数是函数内存地址,CBV的底层也是FBV,as_view是类的绑定方法,自己的类中没有去父类(View)找,as_view()执行完,也是一个内存地址,内存地址是谁的?是闭包函数view的如下源码??@classonlymethod def as_view(cls, **initkwargs): ··· def view(request, *args, **kwargs): ··· return self.dispatch(request, *args, **kwargs) ··· return view# @classonlymethod通过描述符自定义装饰器2、请求来了,路由也匹配成功,执行上边返回的view(requets),加括号调用,并且传入了当次请求的request对象3、然后又返回了当前对象的dispatch方法,自己的名称空间内没有,那么去父类中找,然后发现父类(View)的dispatch方法返回了return handler(request, *args, **kwargs)4、dispatch方法处理请求,什么请求对应什么方法# 父类dispatch方法: def dispatch(self, request, *args, **kwargs): # 请求方法小写如果在当前对象的http_method_names中(八个请求方法) ''' http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] ''' if request.method.lower() in self.http_method_names: # 如果成立,那么执行下面的反射,从当前对象(视图类的对象)拿到请求方法,如果是get请求就拿到get方法,post请求就拿到post方法,然后赋给handler,handler就是相应请求对应的方法,最后返回handler(request),本质其实是get(request) handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: # 不成立,也就是没有该请求对应的方法,执行http_method_not_allowed方法,弹出警告 handler = self.http_method_not_allowed return handler(request, *args, **kwargs) # http_method_not_allowed源码 def http_method_not_allowed(self, request, *args, **kwargs): logger.warning( 'Method Not Allowed (%s): %s', request.method, request.path, extra={'status_code': 405, 'request': request} ) return HttpResponseNotAllowed(self._allowed_methods())getattr(obj,pro,None):按pro判断是否有无obj.pro属性,没有返回None,None可以自定制DRF中最“牛逼”的视图类是APIView,不过也继承了View
导入:from rest_framework.views import APIView
Django中最“牛逼”的视图类是View
导入:
from django.views import View # 最简from django.views.generic import View from django.views.generic.base import View # 最完整'''这三个导入的结果都是一个View,想知道缘由还得看源码和目录结构,如下:'''# 目录结构-django-··· # 此处省略n个包-views-- __init__.py-- generic---- __init__.py---- base.py# 源码分析'''问题:为什么上述不同的py文件都可以指向同一个类?'''我的思路是羊毛出在羊身上,既然都指向类,那么从结果出发是不是更好1、View类在base.py中,那么显而易见最完整的导入是没问题的,重点是为什么简写也可以2、base.py所在的包generic,如果从包中导入py文件我们知道可以通过“白名单”__all__来指定谁可以被其他文件导入,我们发现generic包中指定了:from django.views.generic.base import RedirectView, TemplateView, View和__all__ = [ 'View',·····],到此第二个导入解决# 那么第一个这么短这么简单又是为啥?3、同样在views包的__init__.py文件中发现:from django.views.generic.base import View__all__ = ['View']View类被__all__变量包裹了ps:不管是DRF中的APIView还是乱七八糟的xxView,最后只要继承了Django中的View就是视图类
# 同样和Django中一样写一个视图类,只不过DRF中用APIView底层还是View'''views.py'''from rest_framework.response import Responseclass Test(APIView): def get(self, request): data = {'status': 200, 'msg': 'success'} return Response(data) def post(self, request): data = {'status': 200, 'msg': 'success'} return Response(data)'''urls.py'''path('test/', views.Test.as_view())APIView流程源码分析
1、路由:path('test/', views.Test.as_view()),path第二个参数任然返回函数内存地址,也还是类绑定方法,Test没有as_view方法,去继承的APIView中找,这次不需要和Django一样去View中找了,庆幸的是APIView中有as_view方法,核心源码如下:@classmethod def as_view(cls, **initkwargs): # 校验反射的结果 if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet): def force_evaluation(): raise RuntimeError( 'Do not evaluate the `.queryset` attribute directly, '···· ) ··· # 核心代码 # 这里调用了父类的as_view,查看源码发现又回到了Django中的View类,所以本质还是和上面一样,用到了闭包返回的view view = super().as_view(**initkwargs) ··· # 局部去掉了csrf校验和加装饰器的效果是一样的 return csrf_exempt(view)2、view = super().as_view(**initkwargs),这里跳转了一下,其实看了父类(View)的源码是和上面Django中写视图类继承的View是一样的,这里的(APIView)的as_view只是进行了简单处理和去掉了csrf中间件校验,真实使用的还是View类中的as_view3、然后还是闭包函数的返回值view加括号调用,传入了当前对象的request,继续执行了self.dicpatch(),当前类(Test)没有去父类(APIview)找dispatch方法,发现APIView类中有,千万注意了这里可不是View中的dispatch方法了4、APIView类中的dispatch主要源码:# APIView的dispatch def dispatch(self, request, *args, **kwargs): # request是新的drf提供的request,它是由老的django的request得到的 # 通过老request,生成一个新request,drf的Request的对象 request = self.initialize_request(request, *args, **kwargs) # 把新的request,放到了视图类对象中,可以通过self调用新的request和传入的request是一个,因为放到了self中 self.request = request try: # 执行了三大认证(认证,权限,频率) self.initial(request, *args, **kwargs) # self.http_method_names是个列表 if request.method.lower() in self.http_method_names: # 原来dispatch的核心代码 handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed # 原来dispatch写的,但是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 总结:1、路由:path('test/', views.Test.as_view())2、APIView类的as_view执行,最终使用View类的as_view3、执行闭包返回view加括号调用到此就是as_view加括号调用4、调用执行了view()返回dispatch,但是这里的父类不是View,是APIview所以执行的dispatch是APIView中的dispatch方法5、dispatch方法中包装了新的Request对象,以后视图类中的方法传入的request都是新的,无论三大认证还是视图函数的方法,执行过程中出了异常,都会被处理掉6、dispatch执行完毕返回reponse对象,跳转回进入视图函数继续执行as_view去掉了csrf校验如何包装了新的request?
# APIView中的dispatch方法处理老的requestrequest = self.initialize_request(request, *args, **kwargs)# initialize_request方法,当前类(自己写的)没有去父类找,还是APIView def initialize_request(self, request, *args, **kwargs): """ Returns the initial request object. """ parser_context = self.get_parser_context(request) return Request( request, # 老的request,传入的非处理的,返回后就是新的了 parsers=self.get_parsers(), authenticators=self.get_authenticators(), negotiator=self.get_content_negotiator(), parser_context=parser_context )注意:上面返回的Request对象是rest_framework导入的,然后实例化后返回的,实例化就少不了__init__构造函数from rest_framework.request import Request'''Request类'''class Request: # 这里即将要初始化的request是传入的老的request def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None): ··· # 初始化,将传入的老的request放入新的_request中,所以request._request是老的,self.request._request等价于request._request self._request = request ··· # 验证一下新老requestclass Test(APIView): def get(self, request): data = {'status': 200, 'msg': 'success'} # 新的request print(type(request)) # <class 'rest_framework.request.Request'> print(type(self.request))# <class 'rest_framework.request.Request'> # 老的request print(type(self.request._request)) # <class 'django.core.handlers.wsgi.WSGIRequest'> print(type(request._request)) # <class 'django.core.handlers.wsgi.WSGIRequest'> return Response(data) 新的:<class 'rest_framework.request.Request'>老的:<class 'django.core.handlers.wsgi.WSGIRequest'>三大认证如何执行?
# APIView类处理三大认证self.initial(request, *args, **kwargs)# 核心源码def initial(self, request, *args, **kwargs): ···· self.perform_authentication(request) # 认证 self.check_permissions(request) # 权限 self.check_throttles(request) # 频率 ···# 经过了三大认证才进入了视图函数中间件---路由---···---三大认证---视图函数····# 类似二次校验这里是经过包装后的request
rest_framework.request.Request常用属性和方法
这里的request和原来的Django使用request一样,只是多了一个request.data
request.data:前端POST提交的数据,可以处理多种格式的数据,无论前端传什么编码post提交的数据都在data中
ps:原来提交的数据在request.POST里,有局限性只能处理urlencoded和formdata编码格式,json格式不能处理,是没有request.data的,request其余使用方法的都一样
# 如果用过新包装过的request来调用原来的方法呢?这样给使用者的感觉确实没什么变化源码如下:'''重写了getattr''' def __getattr__(self, attr): try: return getattr(self._request, attr) except AttributeError: return self.__getattribute__(attr)重写getattr的结果是,新的request.method执行的时候本质是request._request.method执行了原来老的request的方法,通过这句getattr(self._request, attr)反射,所以才一样总结:新的request当老的用即可,只是多了个data前端post请求传入的数据,三种编码格式都可以获取
验证处理三种编码格式
from rest_framework.response import Responseclass Test(APIView): def post(self, request): data = {'status': 200, 'msg': 'success'} print(request.POST) print(request._request.POST) print(request.data) return Response(data)验证
# form-data格式结果<QueryDict: {'name': ['hammer']}><QueryDict: {'name': ['hammer']}><QueryDict: {'name': ['hammer']}># urlencode格式结果<QueryDict: {'name': ['hammer']}><QueryDict: {'name': ['hammer']}><QueryDict: {'name': ['hammer']}># json格式结果<QueryDict: {}><QueryDict: {}>{'name': 'Hammer'}官网的一些解释

# 原来的django的request对象中没有data,使得request.data-->无论什么编码格式,post提交数据,data都有值from django.views import Viewfrom django.http import HttpResponsefrom django.utils.decorators import method_decoratorfrom functools import wrapsdef outter(func): @wraps(func) def inner(request,*args,**kwargs): import json if request.POST: request.data = request.POST else: # 将request.body从json对象转换为字典 request.data = json.loads(request.body) res = func(request,*args,**kwargs) return res return inner@method_decorator(outter,name='post')class TestView(View): def get(self, request): return HttpResponse('get请求') def post(self, request): # 只测试了json格式其余都可以也测了 print(request.data) # {'name': 'Hammer'} print(request.POST) # <QueryDict: {}> return HttpResponse('post请求')灵感来源:传送门