drf---APIView源码精读(待补充)

0.dfr的APIView,实际上是继承了django的原生View

一. 路由及视图

    path('text_Apiview/', views.TextAPIView.as_view()),
from rest_framework.views import APIView
from rest_framework.response import Response


class TextAPIView(APIView):
    def get(self, request):
        return Response(data={'msg': 'ok'})

二. 源码精读

1. @classmethod

class APIView(View):
    # APIVie继承了django原生View

    

    @classmethod
    # 1. 相对于diango原生View,APIView用了纯正的类方法
    # 2. 此时as_view是一个类方法
    # 3. 类方法会把其调用者,当作第一个参数传进来

2. as_view---->view

    def as_view(cls, **initkwargs):
        # 1. cls-->由于as_view是一个类的绑定方法,类调用的时候,会把类传进来,
        # 2. 那么cls就是我们当前调用的类--->TextAPIView
        # 3. 他这一步就是把我们的TextView类加括号,实例化成对象(我们下列用TA对象代替他)
        # 4. initkwargs--->一个普通的形参,只是名字好看一点,把它当成**kwargs即可
       
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            # 1. 进行判断,首先通过反射从TA对象获取queryset,然后判断是否是QuerySet对象类型
            def force_evaluation():
                # 2. 是抛异常
                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
            # 1. 把异常封装成TA对象的属性
        view = super().as_view(**initkwargs)
        # 1. 执行父类的(django原生的View)--->我们在CBV源码分析中提到,
        # 2. django原生的as_view其实就是返回了一个view内层函数

3. view = super().as_view(**initkwargs)--->执行其父类的as_view

    def as_view(cls, **initkwargs):
        # 1. initkwargs--->一个普通的形参,只是名字好看一点,把它当成**kwargs即可
        """Main entry point for a request-response process."""
        for key in initkwargs:
            # 1. 这一步循环取值并判断,其实对我们来讲意义不大,只要知道,as_view()支持以字典的形式传参,例如:as_view({'get':'xxx'})
            # 2. 如果该形参有值,进行循环取值,并判断,如果传的值在http_method_names列表中的话,抛异常
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            # 1. 进行判断,比if高级了一点,判断当前传入的值,如果不在当前类中(当前类没有,去其父类,也就是,我们继承的View中找),抛异常
            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):
            # 1. 利用闭包的思想(闭包的最基本的概念:在函数内部定义的变量对外部有调用)
            # 2. 提前说一下,as_view最终返回一个view的内存地址,
            # 3. 我们一开始就说了,django路由规定,路由的第二个参数必须放一个函数的内存地址/include,这个符合django的路由规定
            # 4. 绕了一圈,我们知道,原来as_view(),加括号执行后,居然返回一个内层函数view,并且as_view支持以字典的方式传参
            self = cls(**initkwargs)
            # 1. cls-->由于as_view是一个类的绑定方法,类调用的时候,会把类传进来,
            # 2. 那么cls就是我们当前调用的类--->TextView
            # 3. 他这一步就是把我们的TextView类加括号,实例化成对象(我们下列用TV对象代替他)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                # 1. 判断get是TV对象对象的方法
                # 2. head不是TV对象对象的方法
                self.head = self.get
                # 如果成立给TV对象增加一个实例属性,{'head':'get'}
            self.setup(request, *args, **kwargs)
            # 1. 调用TV对象的setup方法(TV对象中没有,去View中去找)
            if not hasattr(self, 'request'):
                # 1. 老套路,判断request是否是TV对象的属性
                # 2. 不是抛异常
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            # 1. 是的话,执行dispatch方法,
            # 2. 说一下执行流程,首先去对象里面找(没有)--在去类中找(没有)--在去其父类中找(没有)--父类的父类,一直找到根(还没有报错,xxx没有这个属性)
            # 最终返回 handler方法
            # 3. handler-->存的是你在视图中写的 get/post...
            # 4. 或者是 异常方法

            # **************
            # 1.重点来了,我在说一边,执行顺序
            # 2. 首先去对象里面找(没有)--在去类中找(没有)--在去其父类中找(没有)--父类的父类,
            # 3. 一直找到根(还没有报错,xxx没有这个属性)
            # 4. 也就是说现在的dispatch,是APIView中的dispatch(TA对象与类中没有,只能找其父类)
            return self.dispatch(request, *args, **kwargs)

4. return self.dispatch(request, *args, **kwargs)--->父类的dispatch,不在是django原生View

    def dispatch(self, request, *args, **kwargs):
        # 1. APIView中的dispatch
        """
        `.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
        # 1. 当前self--->TV的对象
        # 2. 给TV对象封装数据属性
        request = self.initialize_request(request, *args, **kwargs)
        # 1. 这个request 是新的request,********* 妙  **********
        # 2. APIView对request进行了一层封装,
        # 3. 我解释一下原生的request,request-->经过wsgi解析成一个字典--->django又封装成了一个对象

5. request = self.initialize_request(request, *args, **kwargs)

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)
        # 把django原生的request给当作第一个参数传递给了Request
        return Request(
            request,  # django原生的request
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

6. 我们看看Request是什么(就是封装)

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__)
        )
        # 1. 这个是django原生的request,被重新封装了一下,
        # 2. 也就是说,我们想要在视图中,获取原生的request的话,
        # 3. 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
        self._data = Empty
        self._files = Empty
        self._full_data = Empty
        self._content_type = Empty
        self._stream = Empty

7. 新的reauest,一些属性和方法

a. 重写了__getattr__(目的是为了原生的request的一些属性和方法,继续可以使用,比如,request.GET/POST,当你用句点符操作的时候,会自动执行该魔术方法,他会去原生的request中取值)

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

b. request.data(request.data 感觉是个数据属性,其实是个方法,@property,修饰了
    它是一个字典,post请求不管使用什么编码,传过来的数据,都在request.data)

post:提交的三种编码格式:urlencode,form-data,json,django只能处理前两种,最后一种,需要自己去request.body自己去取

post三种编码格式http://xn--14q306a625a

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

 self._full_data

self._full_data = Empty

 Empty

class Empty:
    """
    Placeholder for unset attributes.
    Cannot use `None`, as that may be a valid value.
    """
    pass

c. request.GET(被重写赋值)

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

三. 待补充的initial(封装完request后进行校验)

# APIView的dispatch方法
    def dispatch(self, request, *args, **kwargs):
        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:
            # 异常模块
            response = self.handle_exception(exc)

        # 渲染模块
        self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response
   
# APIView的initial方法
 	def initial(self, request, *args, **kwargs):
        # 认证组件:校验用户 - 游客、合法用户、非法用户
        # 游客:代表校验通过,直接进入下一步校验(权限校验)
        # 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
        # 非法用户:代表校验失败,抛出异常,返回403权限异常结果
        self.perform_authentication(request)
        # 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色
        # 认证通过:可以进入下一步校验(频率认证)
        # 认证失败:抛出异常,返回403权限异常结果
        self.check_permissions(request)
        # 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
        # 没有达到限次:正常访问接口
        # 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
        self.check_throttles(request)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骑猪去兜风z1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值