源码分析

一. APIView执行流程(源码分析)

派生方法:子类中重写父类中的方法或者新写方法  叫派生。
派生类: 子类

# as_view是django的View的派生方法
def as_view(cls, **initkwargs):

    # 这个view就是原生django中View类的view,还是原来的view
    view = super().as_view(**initkwargs)

    return csrf_exempt(view)
    # (1) csrf_exempt--->去除了所有的csrf---》csrf中间件---》post提交数据,如果不注释---》协带csrf---》以后,无论中间件是否注释,都不需要带了。
    # (2) csrf_exempt装饰器装饰视图函数--->本质跟csrf_exempt(view)一毛一样。
    # (3) 它就是装饰器的本质:把被装饰的函数当做参数,传入装饰器函数,返回值赋值给被装饰的函数

# 重点:1 view还是原来的view,2 以后不用管csrf了

# dispatch 已经不是django原生View的dispatch了,是drf的APIView的dispatch---> 查找顺序的问题
def dispatch(self, request, *args, **kwargs):

    #1 包装了新的request对象, 以后视图类中用的request对象,就是新的request
    request = self.initialize_request(request, *args, **kwargs)

    try:  # 捕获了异常
        # 内部执行了三大认证:认证类,频率类,权限类
        self.initial(request, *args, **kwargs)

        # 原来dispatch的代码,重写了一下---> 执行视图函数
        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)

    # 包装了response对象,响应对象,浏览器看到好看的样子,postman只看到json
    self.response = self.finalize_response(request, response, *args, **kwargs)
    return self.response


#### 重点
   -1 drf:包装了新的request对象(drf的Request的对象了),已经不是django 的request对象了
   -2 在执行视图函数之前,执行了三大认证
   -3 处理了全局异常 ---> 执行三大认证期间,执行视图函数期间,如果出了错,都能统一处理
   -4 包装了响应对象

二. Request对象(源码分析)

1.以后视图类中使用的request对象,是rest_framework.request.Request的对象

2.源码
class Request:
    # 新的: request
    # 老的: request._request

    __init__:
    self._request = request(这个request是老的)
        # 2 以后新的request用起来跟原来一样用
        # 取出原来老request的属性,以下方法取,麻烦
            request._request.POST
            request._request.GET
            request._request.method
        # 重写了__getattr__ 魔法方法, . 拦截  --> 对象.属性如果属性不存在,触发这个方法执行
        # 新的request.POST,新的request中没有POST,触发Request类的__getattr__

        def __getattr__(self, attr):
          try:
              # 通过反射,去拿老request的属性或方法
              return getattr(self._request, attr)
          except AttributeError:
              return self.__getattribute__(attr)

    # 3 新的request有个data方法,包装成了数据属性
        -前端无论什么编码方式传得body体中的数据,都从request.data中取,当字典用即可
        -原生djagno,如果是formdata,urlencoded是从POST中取,如果是json,从body中取出来自己转

    # 4 新的request有个query_params方法,包装成了数据属性
        -就是原来老的request的GET属性
    # 5 上传文件,跟之前一样用,但是人家重写了


 # 新的request重点
	-1 老的request是新的request._request
	-2 跟原来一样用
	-3 body提交的数据都从request.data中取
	-4 请求地址中的数据都从request.query_params中取

image

三. 认证类(源码分析)

-继承 APIView 后,认证是在执行视图类的方法之前执行的
-执行视图类的方法---》dispath中
def dispatch(self, request, *args, **kwargs):
    pass
    # dispatch 具体见 一《APIView执行流程》 中的代码


#2 找到APIView的initial
def initial(self, request, *args, **kwargs):
    #认证
    self.perform_authentication(request)
    #权限
    self.check_permissions(request)
    #频率
    self.check_throttles(request)


# 3 self.perform_authentication(request)
def perform_authentication(self, request):
    request.user # 新的request对象


# 4 Request类中找 user 方法包装成了数据属from rest_framework.request import Request
@property
def user(self):
    if not hasattr(self, '_user'):
        with wrap_attributeerrors():
            # self是 新的request对象
            # 一开始没有,就走了self._authenticate()
            self._authenticate()
    return self._user


# 5  Request类中找_authenticate()
def _authenticate(self):
    # 拿出你配置在视图类上一个个认证类的对象  LoginAuth()
    # authenticator就是LoginAuth()
    for authenticator in self.authenticators:
        try:
            # 为什么咱么要重写 authenticate
            # self是谁?request就是认证类的 def authenticate(self, request):
            # 正常返回两个值:return user, user_token
            # 如果抛了异常:AuthenticationFailed--》捕获了APIException
            user_auth_tuple = authenticator.authenticate(self)
        except exceptions.APIException:
            self._not_authenticated()
            raise
        if user_auth_tuple is not None:#正常返回了两个值
            self._authenticator = authenticator
            # 解压赋值:self是request
            #后续在视图类中 request.user 就是当前登录用户
            self.user, self.auth = user_auth_tuple
            return
    self._not_authenticated()


# 6 咱们视图类中得认证类可以配置多个
    -如果 第一个认证类,就返回了两个值
    -后续的认证类就不走了
    -如果认证类中要返回两个值,视图类中配了多个---》把返回两个值的放在最后
    -返回的两个值,第一个给了request.user,第一个给了request.auth,后续视图类中可以取出来


# 7 self.authenticators ---》 Request中
# Request类实例化得到对象,传入authenticators 最终给了
def __init__(self, request, parsers=None, authenticators=None,negotiator=None, parser_context=None):
    self.authenticators = authenticators or ()

# 8 哪里对Request类实例化了? APIView中
APIView 中dispatch中---》包装了新的request
request = self.initialize_request(request, *args, **kwargs)
def initialize_request(self, request, *args, **kwargs):
    return Request(
        request,
        parsers=self.get_parsers(),
        authenticators=self.get_authenticators(),
        negotiator=self.get_content_negotiator(),
        parser_context=parser_context
    )

# 9 self.get_authenticators()  在APIView中 self是视图类的对象
def get_authenticators(self):
    return [auth() for auth in self.authentication_classes]

四. 权限类(源码分析)

# 0 目标
    1 为什么写一个类继承BasePermission,重写has_permission
        -可以不继承这个类,只重写这个方法也可以
    2 权限类中 self.message 会返回给前端
    3 局部配置:放在视图类中:permission_classes = [CommonPermission]
    4 全局配置,配置在配置文件中也可以--》视图类中就没有
        就会使用:permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
        优先从你项目的配置文件配置的DEFAULT_PERMISSION_CLASSES取,如果没有
        就会去drf的配置文件DEFAULT_PERMISSION_CLASSES取
# 1 从哪里开始找?
-继承APIView后,权限是在执行视图类的方法之前执行的
-执行视图类的方法---》dispath中
def dispatch(self, request, *args, **kwargs):
    try:
        # 三大认证:认证,权限,频率
        self.initial(request, *args, **kwargs)
        ####执行视图类的方法开始###
        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)
    return self.response

#2 找到APIView的initial
def initial(self, request, *args, **kwargs):
    #认证
    self.perform_authentication(request)
    #权限
    self.check_permissions(request)
    #频率
    self.check_throttles(request)

# 3 self.check_permissions(request)---APIView
def check_permissions(self, request):
    # self是 视图类的对象
    # self.get_permissions [CommonPermission(),]--->咱们配置的一个个权限类的对象,放到列表中
    # permission 权限类的对象
    for permission in self.get_permissions():
        # 咱们写的权限类,要重写has_permission,传了俩参数
        # 参数:request 是新的request
        # 参数:self 是?视图类的对象,就是咱么在视图类中写的self,它里面有
        # request和action等
        if not permission.has_permission(request, self):
            # 视图类的对象,没有权限
            self.permission_denied(
                request,
                # 从权限类的对象中反射了message,就是写的给前端看的文字
                message=getattr(permission, 'message', None),
                #响应状态码
                code=getattr(permission, 'code', None)
            )

# 4 self.get_permissions() --> APIView的
def get_permissions(self):
    # 咱么视图类上配置的 permission_classes = [CommonPermission]
    # 返回值是 [CommonPermission(),]--->咱们配置的一个个权限类的对象,放到列表中
    return [permission() for permission in self.permission_classes]


# 5 self.permission_denied  --> APIView中
def permission_denied(self, request, message=None, code=None):
      if request.authenticators and not request.successful_authenticator:
          raise exceptions.NotAuthenticated()
      # 抛了异常,会被全局异常捕获,detai会放在响应体中,code会放在响应状态码中
      raise exceptions.PermissionDenied(detail=message, code=code)

五. 频率类(源码分析)

# 1 从哪里开始找?
-继承APIView后,频率是在执行视图类的方法之前执行的
-执行视图类的方法 ---> dispath中
def dispatch(self, request, *args, **kwargs):
    try:
        # 三大认证:认证,权限,频率
        self.initial(request, *args, **kwargs)
        ####执行视图类的方法开始###
        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)
    return self.response

#2 找到APIView的initial
def initial(self, request, *args, **kwargs):
    #认证
    self.perform_authentication(request)
    #权限
    self.check_permissions(request)
    #频率
    self.check_throttles(request)

# 3
def check_throttles(self, request):
    """
    Check if request should be throttled.
    Raises an appropriate exception if the request is throttled.
    """
    throttle_durations = []

    # 获取配置项中的频率类《见代码 4 》
    for throttle in self.get_throttles():
        # 执行频率类《见代码 5 》
        if not throttle.allow_request(request, self):
            throttle_durations.append(throttle.wait())

    if throttle_durations:
        # Filter out `None` values which may happen in case of config / rate
        # changes, see #1438
        durations = [
            duration for duration in throttle_durations
            if duration is not None
        ]

        duration = max(durations, default=None)
        self.throttled(request, duration)

# 4
def get_throttles(self):
    """
    Instantiates and returns the list of throttles that this view uses.
    """
    # throttle_classes 频率的配置项 《在视图类中配置的就是这个》
    # 视图类中配置 throttle_classes = [] 如果没有配置, 就直接会找全局《见代码 6 》
    return [throttle() for throttle in self.throttle_classes]

# 5
def allow_request(self, request, view):
    # 部分把代码省略
    ...

    # get_cache_key 重写的就是这个方法
    self.key = self.get_cache_key(request, view)
    if self.key is None:
        return True

    # 部分把代码省略
    ....
    return self.throttle_success()

# 6. setting.py 中的配置路径《全局配置》,类属性。
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES

六. 全局异常(源码解析)

点击跳转:https://www.cnblogs.com/gjjcode/articles/17329264.html#3-源码刨析

posted @ 2023-04-05 13:56  codegjj  阅读(5)  评论(0编辑  收藏  举报