DRF-APIView源码分析

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方法。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值