源码分析
一. 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中取
三. 认证类(源码分析)
-继承 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-源码刨析