今天看了drf的五个组件的源码,可读性还是很高的,只是读组件的时候要注意的是 大部分的组件都是由dispatch分发出去的,所以看源码的时候一定要抓住dispatch这条主线,一步一步看下去

一. drf的请求模块(重点)

  1. drf的request是在wsgi的request的基础上进行再次封装

  2. wsgi的request作为drf的request一个属性:_request(下面附源码解释)

#源码:
#在rest-framework 的views.py文件中
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
# 在下面这行代码中 django原来的request传入 self.initialize_request 这个方法
request = self.initialize_request(request, *args, **kwargs) #self.initialize_request方法源码分析
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
# 在下面这个函数的返回结果可以看出来 传入的原生django(wsgi)的request当做初始化参数传入Request这个类中 返回的就是Request这个类的实例化对象
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
) # 由于Request是实例化 所以走的是 __init__方法 下面就是Request的__init__方法代码
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
) self._request = request #只看这一行 我们就可以得出结果 Request实例传进来的原生的django(wsgi)的request在 Request的__init__方法中 重新赋值给self._request 而这个self就是我们在rest-framework中的request
  1. 新的request对旧的request做了完全兼容(下面附源码分析)
#源码
#在rest-framework的request.py中
def __getattr__(self, attr):
"""
If an attribute does not exist on this instance, then we also attempt
to proxy it to the underlying HttpRequest object.
"""
try:
return getattr(self._request, attr) # 通过反射获取属性,如果self._request存在,则使用self._request
except AttributeError: # 不存在 则使用 rest-framework的request
return self.__getattribute__(attr)
  1. 新的request对数据解析更规范化 :所有的拼接参数都解析到 requery_params中,所有数据包数据都被解析到了data中, query_params和 data属于QueryDict类型,可以使用.dict()转化为原生的dict类型

  2. drf的APIView类:重写了as_view(),但主题逻辑还是调用父类View的as_view()方法 ,最大的改动就是局部禁用了csrf认证(下面附源码解释)

 def as_view(cls, **initkwargs):   #目录:rest-framework/views.py
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 view = super().as_view(**initkwargs) # 调用父类View的as_view()方法
view.cls = cls
view.initkwargs = initkwargs
return csrf_exempt(view) # 局部禁用csrf

二. drf的渲染模块(了解)

  1. 可以在视图;类中通过rendere_classes类类属性对该视图的数据响应渲染做配置 --局部配置

  2. 可以在项目的配资文件的drf配置中通过DEFAULT_RENDERER_CLASSES对该视图的数据响应渲染做配置 --全局配置(下面附渲染模块源码分析)

    注:如果在一个视图类在有全局配置下,还进行了局部配置,优先走自己的局部配置

    局部(views中) :renderer_classes = [JSONRenderer, BrowsableAPIRenderer]

    全局(项目配置文件中) : DEFAULT_RENDERER_CLASSES = [JSONRenderer, BrowsableAPIRenderer]

""" 渲染模块源码分析
1、二次处理响应对象:APIView的dispatch方法 - self.finalize_response(request, response, *args, **kwargs)
2、获取渲染类对象:进入finalize_response方法 - self.perform_content_negotiation(request, force=True)
3、从配置文件中得到渲染类对象:perform_content_negotiation -> self.get_renderers() -> [renderer() for renderer in self.renderer_classes]
"""
"""
核心:可以全局和局部配置视图类支持的结果渲染:默认可以json和页面渲染,学习该模块的目的是开发可以全局只配置json方式渲染
""" #详细分析
#位置 rest-framework/views.py中的dispatch函数中 507行
# 下面这行代码利用finalize_response这个函数 对响应对象进行二次处理(规定返回数据的样式)
self.response = self.finalize_response(request, response, *args, **kwargs)
# 再来看看 finalize_response 这个函数做了什么
# 位置:rest-framework/views.py/finalize_response 414行
def finalize_response(self, request, response, *args, **kwargs):
"""
Returns the final response object.
"""
# Make the error obvious if a proper response is not returned
# 下面这行代码的作用是:判断返回的是否是HttpResponseBase的对象,不是则报错
assert isinstance(response, HttpResponseBase), (
'Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` '
'to be returned from the view, but received a `%s`'
% type(response)
) if isinstance(response, Response):
#先通过反射到request中寻找accepted_renderer是否存在 如果不存在,就用自己默认的
if not getattr(request, 'accepted_renderer', None): # 利用perform_content_negotiation函数 找到全局或者默认的渲染规则(项目中没有在 settings中设置全局的渲染规则的话 就用rest-framework默认的渲染规则)
neg = self.perform_content_negotiation(request, force=True)
request.accepted_renderer, request.accepted_media_type = neg response.accepted_renderer = request.accepted_renderer
response.accepted_media_type = request.accepted_media_type
response.renderer_context = self.get_renderer_context() #perform_content_negotiation 函数
# 位置 rest-framework/view.py/perform_content_negotiation 302行
def perform_content_negotiation(self, request, force=False):
renderers = self.get_renderers() #通过get_renderers方法获取配置 # 位置 rest-framework/view.py/get_renderers 256行
def get_renderers(self):
# 又通过renderer_classes 获取配置 通过列表推导式实例化返回
return [renderer() for renderer in self.renderer_classes] # 位置 rest-framework/view.py 107行
# 获取全局DEFAULT_RENDERER_CLASSES配置
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES

三. drf的解析模块 (了解)

作用:服务与数据包数据

  1. 可以在视图类中通过parser_classes类属性对该视图的数据包解析做配置 --局部配置

  2. 可在项目的配置文件的drf配置中通过DEFAULT_PARSER_CLASSES对该视图的数据响应渲染做配置 --全局配置

  3. 解析模块源码分析

    1. APIVIEW的dispatch方法:self.initialize_request(request, *args, **kwargs)内部还提供了数据解析

    2. self.get_parser_context(request)提供要解析的数据,self.get_parsers() 提供解析的类对象(内部从配置中找到解析类)

      核心: 请求的数据包格式会有三种(json urlencoded form-data), drf默认支持三种数据的解析,可以全局或局部配置驶入类具体支持的解析方式

四. 异常模块(重点)

  1. 在settings的drf配置中配置EXCEPTION_HANDLER,指向之定义的exception_handle函数

  2. drf出现异常了,都会回调exception_handle函数,携带异常对象和异常相关信息内容,

    在exception_handle函数完成异常信息的返回以及异常信息的logging日志

  3. 源码分析

    1. 在APIView的dispatch方法中,有一个超大的try...except..., 将代码运行异常都交给异常处理self.handle_exception(exc)

      try:
      self.initial(request, *args, **kwargs) # Get the appropriate handler method
      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 response = handler(request, *args, **kwargs)
      except Exception as exc: #捕捉错误信息 将错误信息交给handle_exception
      response = self.handle_exception(exc)
    2. 从配置中映射出配置处理异常的函数(自定义异常模块就是自定义配置指向自己的函数): self.get_exception_handle()

          def handle_exception(self, exc):
      """
      Handle any exception that occurs, by returning an appropriate response,
      or re-raising the error.
      """
      if isinstance(exc, (exceptions.NotAuthenticated,
      exceptions.AuthenticationFailed)):
      # WWW-Authenticate header for 401 responses, else coerce to 403
      auth_header = self.get_authenticate_header(self.request) if auth_header:
      exc.auth_header = auth_header
      else:
      exc.status_code = status.HTTP_403_FORBIDDEN exception_handler = self.get_exception_handler() #获取错误处理函数 自定义了就用你自定义的 没有自定义就用系统的 context = self.get_exception_handler_context()
      response = exception_handler(exc, context) if response is None:
      self.raise_uncaught_exception(exc) response.exception = True
      return response # drf默认的错误处理函数
      def exception_handler(exc, context):
      if isinstance(exc, Http404):
      exc = exceptions.NotFound()
      elif isinstance(exc, PermissionDenied):
      exc = exceptions.PermissionDenied() if isinstance(exc, exceptions.APIException):
      headers = {}
      if getattr(exc, 'auth_header', None):
      headers['WWW-Authenticate'] = exc.auth_header
      if getattr(exc, 'wait', None):
      headers['Retry-After'] = '%d' % exc.wait if isinstance(exc.detail, (list, dict)):
      data = exc.detail
      else:
      data = {'detail': exc.detail} set_rollback()
      # 处理的错误 就返回错误结果
      return Response(data, status=exc.status_code, headers=headers)
      # 处理不了的就返回none
      return None
    3. 异常函数exception_handle(exc,context)处理异常 就会走自己的先交给系统处理(客户端的异常),系统没处理(服务器异常),再自己处理

      # 自定义异常处理文件可以放在项目任何位置,你找得到就行,不过建议放在根目录或者应用目录
      from rest_framework.response import Response
      from rest_framework.views import exception_handler as drf_exception_handler
      from rest_framework import status
      def exception_handler(exc, context): # 重写错误处理方法
      response = drf_exception_handler(exc, context) #先执行系统定义的错误函数 ,过滤掉请求错误 if response is None: # drf没有处理的异常(服务器异常)
      return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR, data={
      'status': 7,
      'exc': '%s' % exc
      }) # 项目阶段,要记录到日志文件
      return Response(status=response.status_code, data={
      'status': 7,
      # drf处理的客户端异常,原始处理方式是将异常信息放在response对象的data中,data的格式是{'datail': '具体的异常信息'}
      'exc': '%s' % response.data.get('detail')
      })

    核心:异常信息都需要被logging记录,所以需要自定义;drf只处理客户端异常,服务器异常需要手动处理,统一处理结果

五. drf响应模块

Response类生成对象需要的参数,以及Response类的对象可以使用的属性

1. 参数: Response(data=响应的数据,status=响应的网络状态码,headers=想通过响应头再携带不部分信息给前端)
  1. 属性:response.data response.status_code response.status_text

  2. 源码:Response类的____init____方法

        def __init__(self, data=None, status=None,
    template_name=None, headers=None,
    exception=False, content_type=None):
    """
    Alters the init arguments slightly.
    For example, drop 'template_name', and instead use 'data'. Setting 'renderer' and 'media_type' will typically be deferred,
    For example being set automatically by the `APIView`.
    """
    super().__init__(None, status=status) if isinstance(data, Serializer):
    msg = (
    'You passed a Serializer instance as data, but '
    'probably meant to pass serialized `.data` or '
    '`.error`. representation.'
    )
    raise AssertionError(msg) self.data = data
    self.template_name = template_name
    self.exception = exception
    self.content_type = content_type if headers:
    for name, value in headers.items():
    self[name] = value

    核心: 知道response对象可以产生哪些信息,response对象又是如何访问这些信息的(具体的 上面自定义错误信息代码中有使用)

Django-rest Framework(三)的更多相关文章

  1. Django REST framework+Vue 打造生鲜超市(三)

    四.xadmin后台管理 4.1.xadmin添加富文本插件 (1)xadmin/plugins文件夹下新建文件ueditor.py 代码如下: # xadmin/plugins/ueditor.py ...

  2. python 全栈开发,Day94(Promise,箭头函数,Django REST framework,生成json数据三种方式,serializers,Postman使用,外部python脚本调用django)

    昨日内容回顾 1. 内容回顾 1. VueX VueX分三部分 1. state 2. mutations 3. actions 存放数据 修改数据的唯一方式 异步操作 修改state中数据的步骤: ...

  3. Django Rest Framework源码剖析(三)-----频率控制

    一.简介 承接上篇文章Django Rest Framework源码剖析(二)-----权限,当服务的接口被频繁调用,导致资源紧张怎么办呢?当然或许有很多解决办法,比如:负载均衡.提高服务器配置.通过 ...

  4. 用Django Rest Framework和AngularJS开始你的项目

    Reference: http://blog.csdn.net/seele52/article/details/14105445 译序:虽然本文号称是"hello world式的教程&quo ...

  5. Django REST framework+Vue 打造生鲜超市(一)

    一.项目介绍 1.1.掌握的技术 Vue + Django Rest Framework 前后端分离技术 彻底玩转restful api 开发流程 Django Rest Framework 的功能实 ...

  6. Django REST framework+Vue 打造生鲜超市(四)

    五.商品列表页 5.1.django的view实现商品列表页 (1)goods/view_base.py 在goods文件夹下面新建view_base.py,为了区分django和django res ...

  7. Django REST framework+Vue 打造生鲜超市(五)

    六.商品类别数据展示 6.1. 商品类别数据接口 (1)商品分类有两个接口: 一种是全部分类:一级二级三级 一种是某一类的分类以及商品详细信息: 开始写商品分类的接口 (2)序列化 给分类添加三级分类 ...

  8. Django REST framework+Vue 打造生鲜超市(十二)

    十三.首页.商品数量.缓存和限速功能开发  13.1.轮播图接口实现 首先把pycharm环境改成本地的,vue中local_host也改成本地 (1)goods/serializer class B ...

  9. Django rest framework(7)----分页

    目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...

  10. Django rest framework源码分析(3)----节流

    目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...

随机推荐

  1. hive 总结二

    本文参考:黑泽君相关博客 本文是我总结日常工作中遇到的坑,结合黑泽君相关博客,选取.补充了部分内容. 查询函数(Hive高级) NVL(cloumn,replace_with) 如果cloumn为NU ...

  2. <随便写>random函数

    import random random,randint(1,100)返回随机数 random.choice(list)从一个列表中随机选取一个元素返回 random.shuffle(list)将列表 ...

  3. Python-包与常用模块(2)

    目录 包 导入包内包 相对导入和绝对导入 注意事项 time模块 datetime模块 random模块 argparse 模块 configparser模块 hashlib模块和hmac模块 typ ...

  4. 容斥原理——hdu3208

    和hdu2204有点像 这题要特别注意精度问题,如pow的精度需要自己搞一下,然后最大的longlong可以设为1<<31 /* 只要求[1,n]范围内的sum即可 那么先枚举幂次k[1, ...

  5. 第十二章 Odoo 12开发之报表和服务端 QWeb

    报表是业务应用非常有价值的功能,内置的 QWeb 引擎是报表的默认引擎.使用 QWeb 模板设计的报表可生成 HTML 文件并被转化成 PDF.也就是说我们可以很便捷地利用已学习的 QWeb 知识,应 ...

  6. poj3294Life Forms

    传送门 我真是一个垃圾 模板题都不会做 模板题还要别人教 细节写法还要别人教 别人一分钟AC,教我算法还教我写法,最后写出来的别人算法还比我优秀一百倍 数据结构把脑子学傻了,看到题就想怎么用数据结构, ...

  7. python 第三方库的安装方法

    一.看更大的python世界 python 全球计算生态的主站:python 社区 www.pypi.org 二.安装第三方库 1) pip 命令安装方法 1.1 安装第三方库 命令行输入pip in ...

  8. IntelliJ IDEA community 安装教程

    jetbrains官网下载 IntelliJ IDEA安装包 https://www.jetbrains.com/idea/download/#section=windows 此处选择社区版的zip文 ...

  9. 第12章 SQL联接

    第12章 SQL联接 关系数据库的3个支柱:选择.投影和联接. 两种基本的连接同等联接和非同等联接. 源表和目标表有相同的名称的列,就可以在他们之间执行自然联接,而无需指定连接列. 自然join us ...

  10. npm install模块时 报错:not such file or directory

    通过报错信息可以知道,是因为缺少 package.json 这个文件. 解决方法: 首先,初始化项目,一路回车就行 npm init -f 接着安装依赖 npm install formidable ...