DRF-APIView源码分析
路由和视图
urlpatterns = [ path("api_view/", views.API_view.as_view())]
from rest_framework.views import APIView
from rest_framework.response import Response
class API_view(APIView):
def get(self, request):
return Response({"code": 201,'msg': 'ok'})
源码精读
@classmethod : 我们知道对于一个普通的类,我们要使用其中的函数的话,需要对类进行实例化,而一个类中,某个函数前面加上了classmethod的话,那么这个函数就可以不通过实例化直接调用
@classonlymethod : 继承了@classmethod
class APIView(View):
@classmethod
def as_view(cls, **initkwargs):
# 1. 进行判断,首先通过反射从TA对象获取queryset,然后判断是否是QuerySet对象类型
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.'
)
# 把异常封装成对象属性(这里的对象就是API_view)
cls.queryset._fetch_all = force_evaluation
# 2.继承Django的View类 调用使用父类方法 进入父类中参看
view = super().as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# 免除Django的csrf校验
return csrf_exempt(view)
# 3.这里是View中的as_view()
@classonlymethod
def as_view(cls, **initkwargs):
# 对传入的参数做简单的校验,避免传入的参数将类自己的关键函数名覆盖掉,或者传入类中没定义的属性。开头这个 for 循环就是干这个用的。
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError(
'The method name %s is not accepted as a keyword argument '
'to %s().' % (key, cls.__name__)
)
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
####### 重点 #######
def view(request, *args, **kwargs):
# 3.1 实例化类自己并赋值给 self
self = cls(**initkwargs)
# 3.2 接着调用 self.setup() 对实例的属性进行了初始化
self.setup(request, *args, **kwargs)
# 判断request是否是对象的属性(这儿对象就是API_view)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
### 注意 ###
# 因为APIView重写了dispatch 根据mro顺序应该优先执行当前类的dispatch()方法 所以我们看APIView中的dispatch方法干了什么
return self.dispatch(request, *args, **kwargs)
# 闭包返回 这个函数的地址
return view
# 4.APIView()重新后的dispatch方法
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# 4.1 这里对原生Django中的request进行了重新封装 进去看看具体怎么封装的
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers
try:
# 5. 三大模块的认证方式
self.initial(request, *args, **kwargs)
# 通过反射获取视图中get/post...等方法
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
# 6. 响应模块 执行相关方法
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
# 4.1.1
def initialize_request(self, request, *args, **kwargs):
# get_parser_context 就返回{视图对象, URL路由参数}
parser_context = self.get_parser_context(request)
# 返回的是一个实例化Request 进Request这个类看是怎么玩的
return Request(
# 把django原生的request给当作第一个参数传递给了Request
request,
# 实例化解析数据相关类
parsers=self.get_parsers(),
# 实例化认证认证相关类
authenticators=self.get_authenticators(),
# 实例化匹配对应的解析器对象
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
# 4.1.2
class Request:
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__)
)
# 把Django的request变为了self._request
# 也就是说,我们想要在视图中,获取原生的request的话 是request._request 才是原生的
self._request = request
self.parsers = parsers or ()
self.authenticators = authenticators or ()
self.negotiator = negotiator or self._default_negotiator()
self.parser_context = parser_context
......
# 重写了__ getattr __方法 这样可以和以前一样使用request
def __getattr__(self, attr):
try:
# 利用getattr方法 如果不存在就去self._request中找
return getattr(self._request, attr)
except AttributeError:
# 没找到就__getattribute__报错
return self.__getattribute__(attr)
# 获取get请求传参
@property
def query_params(self):
return self._request.GET
# 获取post数据传参
# 以通过request.data[字典类型]取出post 如果是json得去request.body去取
@property
def data(self):
if not _hasattr(self, '_full_data'):
self._load_data_and_files()
return self._full_data
# post文件数据
@property
def FILES(self):
# Leave this one alone for backwards compat with Django's request.FILES
# Different from the other two cases, which are not valid property
# names on the WSGIRequest class.
if not _hasattr(self, '_files'):
self._load_data_and_files()
return self._files
# 5.1 回到APIView()重新后的dispatch方法 我们看看 下一步initial()干了啥 具体三大组件怎么实现后面聊
def initial(self, request, *args, **kwargs):
# 获取请求的后缀格式
self.format_kwarg = self.get_format_suffix(**kwargs)
# 确定使用哪种渲染器和媒体类型来渲染响应,并存储 渲染器就是最后数据以何种方式展示
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# determine_version 返回一个元组 ( 调用对象的determine_version()方法,对象 )
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# 认证组件
self.perform_authentication(request)
# 权限组件
self.check_permissions(request)
# 频率组件
self.check_throttles(request)
- 总结:drf中重写了原生django中的as_view()方法,使用APIView中的as_view(),同时在内部重新封装了requset,和覆盖了父类View中的dispatch方法。