码迷,mamicode.com
首页 > 其他好文 > 详细

【Django】DRF源码分析之五大模块

时间:2020-06-14 16:25:00      阅读:56      评论:0      收藏:0      [点我收藏+]

标签:min   format   exception   过程   **kwargs   asa   anti   重写   attribute   

纸上得来终觉浅,绝知此事要躬行。

前言

在之前的DRF源码分析对比原生Django介绍了二者的区别,最后分析得出DRF对原生的dispatch方法做了很多改进,本章就接着分析APIView下的dispatch到底做了那些事?

通过上次的分析主要分为以下几个模块:

  • 请求模块
  • 认证模块(本次不做介绍)
    • 认证
    • 权限
    • 限流
  • 响应模块
  • 异常模块
  • 渲染模块

请求模块

  1. 源码入口,rest_framework/views.py下的APIView类中的dispatch方法:request = self.initialize_request(request, *args, **kwargs)ctrl+b进入:
    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)
        
        # 返回Request类的对象
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
  1. 进入Request类的源码,查看实例化的过程简化版:
class Request:
    def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
        ......
        # 二次封装request,将原生request作为drf Request 对象的 _request属性
        self._request = request
        ......

    @property
    def query_params(self):
        """
        More semantically correct name for request.GET.
        """
        return self._request.GET

    @property
    def data(self):
        if not _hasattr(self, ‘_full_data‘):
            self._load_data_and_files()
        return self._full_data

    def __getattr__(self, attr):
        try:
            return getattr(self._request, attr)
        except AttributeError:
            return self.__getattribute__(attr)

分析:

  1. 将wsgi的request对象转化成drf的Request类的对象
  2. 封装后的request对象完全兼容wsgi的request对象,并且将原request保存在新request._request
  3. 重写格式化请求数据存放位置

例如获取请求参数:

print(request._request.method)  # 在内部将wsgi的request赋值给request._request
print(request.method)  # 就是通过__getattr__走的是request._request.method
print(request.query_params)  # 走的是方法属性,就是给request._request.GET重新命名
print(request.data)  # 走的是方法属性,值依赖于request._full_data

通过源码和分析,我们了解到在dispatch中走到request = self.initialize_request(request, *args, **kwargs)调用initialize_request方法返回的是rest_framework/request.py下的Request类的对象,这个类在实例化的过程中对原生参数request做了进一步封装和兼容,最终在返回到dispath中的request,整个请求模块彻底走完。

响应模块

  1. 接下来dispatch运行到响应模块,代码如下:

技术图片

关键代码实现以及变量定义如下:

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

def http_method_not_allowed(self, request, *args, **kwargs):
     """
     If `request.method` does not correspond to a handler method,
     determine what kind of exception to raise.
     """
     raise exceptions.MethodNotAllowed(request.method)

通过判断类视图是否定义过http_method_names中的方法,不存设置handler=http_method_not_allowed,而http_method_not_allowed内部是直接抛出异常。

  1. 条件满足时,通过getattr反射机制,handler就是我们类视图定义的方法,执行response = handler(request, *args, **kwargs),返回response,原生的Django也是这样做的。

异常模块

  1. 当上面的步骤出现错误时,异常捕获就会走到异常模块self.handle_exception(exc),点击参看源码,我做了一定的简化:
    def handle_exception(self, exc):
        ......
        # 获取处理异常的方法
        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

    def get_exception_handler(self):
        """
        Returns the exception handler that this view uses.
        """
        return self.settings.EXCEPTION_HANDLER
  1. 最后其实返回的是self.settings.EXCEPTION_HANDLER,点击进入源码,我们可以发现:
settings = api_settings

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)

‘EXCEPTION_HANDLER‘: ‘rest_framework.views.exception_handler‘,

所以真正的异常处理是在rest_framework.views.exception_handler这个函数

技术图片

  1. 接下来异常处理走完,判断exception_handler返回的结果进行处理:
if response is None:
    # 没有异常内容,抛出异常信息
    self.raise_uncaught_exception(exc)
# 有异常内容,返回异常内容
response.exception = True
return response
  1. 至此异常模块也已经走完,通过上面的源码分析我们可以得知,如果我们想要自定义异常,只需要重新实现exception_handler函数,并且在settings.py文件中指定exception_handler函数的位置。

例如:

# 自定义drf配置
REST_FRAMEWORK = {
    # 全局配置异常模块
    ‘EXCEPTION_HANDLER‘: ‘api.exception.exception_handler‘,
}

========================下面就是导入原始的函数,然后先实现其原有的逻辑,之后在处理自己的逻辑====================
from rest_framework.views import exception_handler as drf_exception_handler # 防止与自定义的重名,起一个别名

def exception_handler(exc, context): # 注意名称必须为`exception_handler`
    response = drf_exception_handler(exc, context)
    if response is None:

        view = context[‘view‘]
        if isinstance(exc, DatabaseError) or isinstance(exc, RedisError):
            # logger.error(‘[%s] %s‘ % (view, exc))
            response = Response({‘detail‘: ‘服务器内部错误‘}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
    return response

渲染模块

  1. 从前面的请求===>响应===>异常,到现在的渲染,也就是dispatch的最后一部分,源码如下:
def dispatch(self, request, *args, **kwargs):
    ......
    # 渲染模块
    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response
  1. 点击finalize_response源码查看,进行一定的简化:
    def finalize_response(self, request, response, *args, **kwargs):
        """
        Returns the final response object.
        """
        if isinstance(response, Response):
            if not getattr(request, ‘accepted_renderer‘, None):
                neg = self.perform_content_negotiation(request, force=True) # 获取解析类的对象
                request.accepted_renderer, request.accepted_media_type = neg # 拆包
        ......
  1. 点击进入perform_content_negotiation:
    def perform_content_negotiation(self, request, force=False):
        """
        Determine which renderer and media type to use render the response.
        """
        renderers = self.get_renderers() # 获得解析类对象
        conneg = self.get_content_negotiator()

        try:
            return conneg.select_renderer(request, renderers, self.format_kwarg)
        except Exception:
            if force:
                return (renderers[0], renderers[0].media_type)
            raise
  1. 进入get_renderers查看:
    def get_renderers(self):
        """
        Instantiates and returns the list of renderers that this view can use.
        """
        return [renderer() for renderer in self.renderer_classes]

renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
  1. api_settings中获取渲染模块的配置:
‘DEFAULT_RENDERER_CLASSES‘: [
        ‘rest_framework.renderers.JSONRenderer‘, # 只显示出json数据
        ‘rest_framework.renderers.BrowsableAPIRenderer‘, # 渲染出页面
    ],

至于渲染类我们就不做过多的分析,分析到这我们发现渲染类和异常类很相似,同样我们可以在settings.py中配置,例如:项目上线之后我们只希望返回json格式的数据,因此可以定义如下配置:

# 自定义drf配置
REST_FRAMEWORK = {
    # 全局渲染类配置
    ‘DEFAULT_RENDERER_CLASSES‘: [
        ‘rest_framework.renderers.JSONRenderer‘,
        # ‘rest_framework.renderers.BrowsableAPIRenderer‘,
    ]
}

当然我们在settings.py中配置都是全局配置,也可以在CBV的类视图中定义,因为我们继承了APIView,下面我们可以查看一下源码的配置:

class APIView(View):
    
    # The following policies may be set at either globally, or per-view.
    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES # 渲染
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES # 解析
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES # 认证
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES # 限流
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES # 权限
    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
    metadata_class = api_settings.DEFAULT_METADATA_CLASS
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

这些配置项都在 rest_framework/settings.py下,有兴趣可以去查看,而且DRF的封装也比较规范,可以导入特定的类:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.serializers import Serializer
from rest_framework.settings import APISettings
from rest_framework.filters import SearchFilter     #过滤
from rest_framework.pagination import PageNumberPagination  #分页
from rest_framework.authentication import TokenAuthentication   #认证
from rest_framework.permissions import IsAuthenticated   #权限(是否登录)
from rest_framework.throttling import SimpleRateThrottle  #频率
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer # 渲染

所以我们在类视图可以如下定义:

from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer

class User(APIView):
    # 局部渲染配置
    renderer_classes = [JSONRenderer,BrowsableAPIRenderer]

    def get():
        pass
    ......

OK,看到这里,差不多整个dispatch的代码执行流程全部走完了,除了一个三大认证以外,我打算用另一篇博文分析,休息一下继续肝。

相关参考:http://www.manongjc.com/detail/14-zbmsyiqdjwviznh.html

【Django】DRF源码分析之五大模块

标签:min   format   exception   过程   **kwargs   asa   anti   重写   attribute   

原文地址:https://www.cnblogs.com/ydongy/p/13125065.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!