DjangoDRF源码分析之五大模块
Posted ydongy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DjangoDRF源码分析之五大模块相关的知识,希望对你有一定的参考价值。
纸上得来终觉浅,绝知此事要躬行。
前言
在之前的DRF源码分析对比原生Django介绍了二者的区别,最后分析得出DRF
对原生的dispatch
方法做了很多改进,本章就接着分析APIView
下的dispatch
到底做了那些事?
通过上次的分析主要分为以下几个模块:
- 请求模块
- 认证模块(本次不做介绍)
- 认证
- 权限
- 限流
- 响应模块
- 异常模块
- 渲染模块
请求模块
- 源码入口,
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
)
- 进入
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)
分析:
- 将wsgi的request对象转化成drf的Request类的对象
- 封装后的request对象完全兼容wsgi的request对象,并且将原request保存在新request._request
- 重写格式化请求数据存放位置
例如获取请求参数:
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
,整个请求模块彻底走完。
响应模块
- 接下来
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
内部是直接抛出异常。
- 条件满足时,通过
getattr
反射机制,handler
就是我们类视图定义的方法,执行response = handler(request, *args, **kwargs)
,返回response
,原生的Django也是这样做的。
异常模块
- 当上面的步骤出现错误时,异常捕获就会走到异常模块
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
- 最后其实返回的是
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
这个函数
- 接下来异常处理走完,判断
exception_handler
返回的结果进行处理:
if response is None:
# 没有异常内容,抛出异常信息
self.raise_uncaught_exception(exc)
# 有异常内容,返回异常内容
response.exception = True
return response
- 至此异常模块也已经走完,通过上面的源码分析我们可以得知,如果我们想要自定义异常,只需要重新实现
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
渲染模块
- 从前面的请求===>响应===>异常,到现在的渲染,也就是
dispatch
的最后一部分,源码如下:
def dispatch(self, request, *args, **kwargs):
......
# 渲染模块
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
- 点击
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 # 拆包
......
- 点击进入
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
- 进入
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
- 从
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
的代码执行流程全部走完了,除了一个三大认证以外,我打算用另一篇博文分析,休息一下继续肝。
以上是关于DjangoDRF源码分析之五大模块的主要内容,如果未能解决你的问题,请参考以下文章
Android4.42-Setting源码分析之蓝牙模块Bluetooth(下)
从五大结构体,带你掌握鸿蒙轻内核动态内存Dynamic Memory
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段