不会DRF?源码都分析透了确定不来看?

可以更方便的使用django写出符合restful规范的接口

下载安装

  • pip3 install djangorestframework
  • pycharm下载

注意

  • rest_framework是一个app需要注册!

官网:https://www.django-rest-framework.org/

  • drf安装默认安装的最新版本,如果django版本过低会自动升级到3.x版本

  • 版本支持对应关系

示例

'''views.py'''
from rest_framework.views import APIView
from rest_framework.response import Response class 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 跨站请求伪造


快速使用DRF写出接口

序列化和反序列化

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

  • 序列化:把我们语言识别的数据转换成指定的格式提交给别人(前端)

    • 比如python中的字典,列表,对象等转json,xml,prop····
  • 反序列化:把别人提供的数据转换成我们所需的格式
    • 最常见的比如我们使用json模块来对数据进行处理····

在Djangorestframework中的序列化反序列化又是如何?

  • 序列化: 在Django中获取到的数据默认是模型对象(QuerySet对象),但是模型对象数据无法直接提供给前端或别的平台使用,我们需要把数据进行序列化,变成字符串或json数据提供给前端或其他平台;
  • 反序列化: 前端或其他平台传入数据到后台,比如是json格式字符串,后端需要存入数据库,需要转换成python中的对象,然后处理存入数据库;
  • 总结:
    • python对象 -----》json格式字符串 (序列化)
    • json格式字符串 -----》 python 对象 (反序列化)

如何使用DRF快速写出增删查改的接口?

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的时候,地址严格写,不能缺少/
'''

views.py

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

serializer.py

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

urls.py

from rest_framework.routers import SimpleRouter
from app import views router = 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列表中,那样就不是注释了,成字符串了!!!

在settings的app中注册

INSTALLED_APPS = [
'rest_framework'
]

models.py

from django.db import models

class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5,decimal_places=2)
authors = models.CharField(max_length=32)

postman测试

获取所有

获取一条

新增一条数据

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

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

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


CBV源码流程分析

因为DRF框架里大部分都是基于CBV(视图类)写,所以流程是什么,如何执行需要了解,同时也方便理解APIView,顺便提一嘴APIView也是继承了View ----> class APIView(View)

这里需要强调一下,CBV路由归根结底还是FBV都是函数的内存地址,比如views.类.as_view()底层仍然是函数的内存地址

CBV源码执行流程

'''views.py'''
from django.views import View
from django.http import HttpResponse class 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可以自定制
  • 反射忘记的可以看俺的这篇博客:python 多态、组合、反射

Django View和DRF APIView的小插曲

  • 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就是视图类

DRF之APIView和Request对象分析

APIView的执行流程

# 同样和Django中一样写一个视图类,只不过DRF中用APIView底层还是View
'''views.py'''
from rest_framework.response import Response
class 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_view
3、然后还是闭包函数的返回值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_view
3、执行闭包返回view加括号调用到此就是as_view加括号调用
4、调用执行了view()返回dispatch,但是这里的父类不是View,是APIview所以执行的dispatch是APIView中的dispatch方法
5、dispatch方法中包装了新的Request对象,以后视图类中的方法传入的request都是新的,无论三大认证还是视图函数的方法,执行过程中出了异常,都会被处理掉
6、dispatch执行完毕返回reponse对象,跳转回进入视图函数继续执行as_view去掉了csrf校验

如何包装了新的request?

# APIView中的dispatch方法处理老的request
request = 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
··· # 验证一下新老request
class 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对象分析

这里是经过包装后的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请求传入的数据,三种编码格式都可以获取

验证处理三种编码格式

  • json格式,只有request.data可以获取,结果是字典
  • form-data格式和urlencode格式都可以获取并且是QueryDict对象
from rest_framework.response import Response
class 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,造一个!

# 原来的django的request对象中没有data,使得request.data-->无论什么编码格式,post提交数据,data都有值
from django.views import View
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from functools import wraps
def 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请求')

灵感来源:传送门

不会DRF?源码都分析透了确定不来看?的更多相关文章

  1. DRF源码系列分析

    DRF源码系列分析 DRF源码系列分析--版本 DRF源码系列分析--认证 DRF源码系列分析--权限 DRF源码系列分析--节流

  2. Django之DRF源码分析(二)---数据校验部分

    Django之DRF源码分析(二)---数据校验部分 is_valid() 源码 def is_valid(self, raise_exception=False): assert not hasat ...

  3. 【Orleans开胃菜系列2】连接Connect源码简易分析

    [Orleans开胃菜系列2]连接Connect源码简易分析 /** * prism.js Github theme based on GitHub's theme. * @author Sam Cl ...

  4. MapReduce的ReduceTask任务的运行源码级分析

    MapReduce的MapTask任务的运行源码级分析 这篇文章好不容易恢复了...谢天谢地...这篇文章讲了MapTask的执行流程.咱们这一节讲解ReduceTask的执行流程.ReduceTas ...

  5. MapReduce的MapTask任务的运行源码级分析

    TaskTracker任务初始化及启动task源码级分析 这篇文章中分析了任务的启动,每个task都会使用一个进程占用一个JVM来执行,org.apache.hadoop.mapred.Child方法 ...

  6. TaskTracker任务初始化及启动task源码级分析

    在监听器初始化Job.JobTracker相应TaskTracker心跳.调度器分配task源码级分析中我们分析的Tasktracker发送心跳的机制,这一节我们分析TaskTracker接受JobT ...

  7. MongoDB源码分析——mongod程序源码入口分析

    Edit 说明:第一次写笔记,之前都是看别人写的,觉得很简单,开始写了之后才发现真的很难,不知道该怎么分析,这篇文章也参考了很多前辈对MongoDB源码的分析,也有一些自己的理解,后续将会继续分析其他 ...

  8. FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  9. LinkedHashMap 源码详细分析(JDK1.8)

    1. 概述 LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题.除此之外,Linke ...

随机推荐

  1. 带分数--第四届蓝桥杯省赛C++B/C组

    第四届蓝桥杯省赛C++B/C组----带分数 思路: 1.先枚举全排列 2.枚举位数 3.判断是否满足要求 这道题也就是n=a+b/c,求出符合要求的abc的方案数.进行优化时,可以对等式进行改写,改 ...

  2. MyBatis辅助功能点三:延迟加载

    延迟加载即先加载必需信息,然后再根据需要进一步加载信息的方式.实际应用如:常出现先查询表A,再根据表A的输出结果查询表B的情况.而有些时候,从A表查询出来的数据,只有一部分要查询表B.这时用延迟加载就 ...

  3. 继承及super关键字

    继承 继承的本质是对某一批类的抽象,从而实现对世界更好的建模 extend的意思是"扩展",子类是父类的扩展. Java中类只有单继承,没有多继承:儿子只能有一个亲生爸爸,一个爸爸 ...

  4. service与systemctl命令比较

    本文将比较 linux 的 service 和 systemctl 命令,先分别简单介绍这两个命令的基础用法,然后进行比较. 从 CentOS 7.x 开始,CentOS 开始使用 systemd 服 ...

  5. 【01】Maven依赖插件之maven-dependency-plugin

    一.插件目标(goal) 1.analyze:分析项目依赖,确定哪些是已使用已声明的,哪些是已使用未声明的,哪些是未使用已声明的 2.analyze-dep-mgt:分析项目依赖,列出已解析的依赖项与 ...

  6. syntax error: unexpected end of file完美解决方案

    用shell编写的一个脚本,执行的时候报错:syntax error: unexpected end of file! 发生这种报错有两种原因: 第一种: 如果你是用 windows 系统编写的脚本, ...

  7. 使用Oracle的PROFILE对用户资源限制和密码限制

    转至:https://blog.csdn.net/zhuomingwang/article/details/6444240?utm_medium=distribute.pc_relevant.none ...

  8. selenium+python自动化102-登录页面滑动解锁(ActionChains)

    前言 登录页面会遇到滑动解锁,滑动解锁的目的就是为了防止别人用代码登录(也就是为了防止你自动化登录),有些滑动解锁是需要去拼图这种会难一点. 有些直接拖到最最右侧就可以了,本篇讲下使用 seleniu ...

  9. QQ音乐官方定制精简版v1.3.6 纯净无广告

    介绍 近期腾讯推出了QQ音乐简洁版.顾名思义,QQ音乐简洁版就是官方精简后的版本,没有内置任何广告.完全专注于听歌,不存在直播.K歌.短视频等花里胡哨的内容.如有违规,请删删.. 结尾附pc端 QQ音 ...

  10. jmeter-获取系统最大并发数

    问题:有一个页面,需要测试一下最大支持多少用户并发? 此时需计算的是最大用户并发数,强调的是同时操作,也可以理解为同时发起请求: 针对这个问题,我们可以通过rps定时器或阶梯加压线程组测试每秒最大的请 ...