权限、认证、频率、过滤、排序、分页、全局异常源码分析
权限源码
if not permission.has_permission(request, self) 这里的self并不是指对象,指的是视图类的对象(view),视图类中的has_permission中有三个参数(self,request,view)
配置在视图类上一个个权限类的对象列表 [权限对象1,权限对象2]
认证源码
所以要从根上找
从request中找,去Request类中找user---》方法包装成了数据属性
因为认证类抛异常继承的是APIException,所以将只要符合APIException全部捕获。
self就是request对象,后续 request.user就是认证类返回的第一个参数
认证类可以配置多个,但是如果有一个返回了,后续的就不走了
self.authenticators 是request对象的属性,是在Request实例化的时候传入的,它什么时候实例化的,包装新的Reqeust时传入的---》APIView的dispatch--》
频率源码分析
频率类要写:
1、写一个类,继承BaseThrottle
2、在类中重写:allow_request方法,传入三个参数
3、在allow_request写限制逻辑,如果能访问,返回 True
4、如果超了次数,就不能访问,返回False
5、局部配置在视图类上
6、全局配置在配置文件中
我们在drf中写的时候,不需要继承BaseThrottle,继承了SimpleRateThrottle,重写get_cache_key
例子:
自定义频率类,实现一分钟只能访问三次的控制
(1)取出访问者ip
(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
SimpleRateThrottle 源码分析
补第一张图的文字说明
自定义频率类
逻辑思路和源码一致
from rest_framework.throttling import BaseThrottle class MyThrottle(BaseThrottle): # VISIT_RECORD = {'192.168.1.1':[当前时间,当前时间,访问时间列表]} VISIT_RECORD = {} #空字典 def __init__(self): self.history = [] def allow_request(self, request, view): ip = request.META.get('REMOTE_ADDR') import time ctime = time.time() # (2)判断当前ip不在访问字典里,说明是第一次访问,添加进去,并且直接返回True,表示第一次访问 if ip not in self.VISIT_RECORD: self.VISIT_RECORD[ip] = [ctime, ] # VISIT_RECORD = {'192.168.1.1':[当前时间2,当前时间1,]} return True # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间, self.history = self.VISIT_RECORD[ip] # 访问时间列表 while self.history and ctime - self.history[-1] > 60: # 循环删除1分钟之前访问的实际 self.history.pop() # 最后self.history都剩下是一分钟之内的时间了 # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过 # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败 if len(self.history) < 3: self.history.insert(0, ctime) return True else: return False def wait(self): import time ctime = time.time() return 60 - (ctime - self.history[-1])
过滤源码分析
ListModelMixin---》list方法
取出一个个配置在视图类上的过滤类,依次实例化得到对象后执行对象的filter_queryset方法完成过滤---》最终返回的数据,就是过滤后的数据
排序源码分析
针对于查询所有接口---》继承:GenericAPIView+ListModelMixin---》只需要在视图类中写一个类属性---》filter_backends = [排序类]
分页源码分析
GenericAPIView+ListModelMixin
class Pager(APIView): def get(self,request,*args,**kwargs): # 获取所有数据 ret=models.Book.objects.all() # 创建分页对象 page=PageNumberPagination() # 在数据库中获取分页的数据 page_list=page.paginate_queryset(ret,request,view=self) # 对分页进行序列化 ser=BookSerializer1(instance=page_list,many=True) # return Response(ser.data) # 这个也是返回Response对象,但是比基本的多了上一页,下一页,和总数据条数(了解即可) return page.get_paginated_response(ser.data) return Response(ser.data) # 只会有数据,不会有上一页和下一页,总条数
全局异常处理
exception_handler就是配置文件中配置的一个函数-->默认的
默认执行的是:
rest_framework.views.exception_handler函数---》只处理了drf的异常
1、写一个函数在内部处理
from rest_framework.views import exception_handler def common_exception_handler(exc,context): res=exception_handler(exc,context) if res: #这次异常是drf的,并且它处理了 # 我们要统一返回格式 return Response({'code':888,'msg':"系统异常(drf异常),请联系系统管理员:%s"%res.data.get('detail',None)}) else: return Response({'code': 999, 'msg': "系统异常(非drf异常),请联系系统管理员:%s" % str(exc)})
2、配置在配置文件上