断点调试使用
# 程序在debug模式运行,可以在任意位置停下,查看当前情况下变量数据的变化情况 # 使用pycharm调试程序 - 1.以debug模式运行、 - 2.在代码左侧加入断点(红圈) - 3.step over 单步调试 - 4.step into 进入该函数内部运行 - 5.绿色箭头 快速调到下一个断点
认证,权限,频率源码分析
权限类的执行源码
# 权限的源码执行流程 - 写一个权限类,局部使用,配置在视图类的,就会执行权限类的has_permission方法,完成权限校验 #drf的apiview,在执行视图类的方法之前,执行了三大认证 ---> dispatch方法中的 - self.initial(request, *args, **kwargs) ---> 执行3大认证 # # APIView类的399行左右: 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 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) # APIView的326 左右 def check_permissions(self, request): # self.get_permissions()----》[CommonPermission(),] # permission 是我们配置在视图类上权限类的对象 for permission in self.get_permissions(): # 权限类的对象,执行has_permission,这就是为什么我们写的权限类要重写has_permission方法 # self 是视图类的对象,就是咱们自己的的权限类的has_permission的view参数 if not permission.has_permission(request, self): # 如果return 的是False,就会走这里,走这里是,没有权限 # 如果配了多个权限类,第一个没过,直接不会再执行下一个权限类了 self.permission_denied( request, message=getattr(permission, 'message', None), code=getattr(permission, 'code', None) ) # APIView的274行左右 get_permissions def get_permissions(self): # self.permission_classes 是咱们配置在视图类上的列表,里面是一个个的权限类,没加括号 # permission_classes = [CommonPermission] # [CommonPermission(),] 本质返回了权限类的对象,放到列表中 return [permission() for permission in self.permission_classes] ## 总结: -APIView---dispatch----》initial---》倒数第二行---》self.check_permissions(request) 里面取出配置在视图类上的权限类,实例化得到对象,一个个执行对象的has_permission方法,如果返回False,就直接结束,不再继续往下执行,权限就认证通过 -如果视图类上不配做权限类:permission_classes = [CommonPermission],会使用配置文件的api_settings.DEFAULT_PERMISSION_CLASSES 优先使用项目配置文件,其次使用drf内置配置文件
认证源码分析
# 之前读过:drf的apiview,在执行视图类的方法之前,执行了3大认证----》dispatch方法中的 -497行左右, self.initial(request, *args, **kwargs)---》执行3大认证 # APIView类的399行左右: 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 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) # APIView的316行左右 def perform_authentication(self, request): request.user #咱们觉得它是个属性,其实它是个方法,包装成了数据属性 # Request类的user方法 219行左右 @property def user(self): if not hasattr(self, '_user'): with wrap_attributeerrors(): self._authenticate() return self._user # self 是Request的对象,找Request类的self._authenticate() 373 行 def _authenticate(self): # self.authenticators 我们配置在视图类上认证类的一个个对象,放到列表中 # Request类初始化的时候,传入的 for authenticator in self.authenticators: try: # 返回了两个值,第一个是当前登录用户,第二个的token,只走这一个认证类,后面的不再走了 # 可以返回None,会继续执行下一个认证类 user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise if user_auth_tuple is not None: self._authenticator = authenticator # 解压赋值: #self.user=当前登录用户,self是当次请求的新的Request的对象 #self.auth=token self.user, self.auth = user_auth_tuple return self._not_authenticated() # self.authenticators 去Request类的init中找 152行左右 def __init__(self, request, parsers=None, authenticators=None, negotiator=None, parser_context=None): ..... self.authenticators = authenticators or () ..... # 什么时候调用Reqeust的__init__?---》APIVIew的dispatch上面的492行的:request = self.initialize_request(request, *args, **kwargs)-----》385行----》 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 ) # 总结: 1 配置在视图类上的认证类,会在执行视图类方法之前执行,在权限认证之前执行 2 自己写的认证类,可以返回两个值或None 3 后续可以从request.user 取出当前登录用户(前提是你要在认证类中返回)
频率源码分析
# 之前读过:drf的apiview,在执行视图类的方法之前,执行了3大认证----》dispatch方法中的 -497行左右, self.initial(request, *args, **kwargs)---》执行3大认证 # APIView类的399行左右: 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 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) # APIView 的352行 def check_throttles(self, request): throttle_durations = [] #self.get_throttles() 配置在视图类上的频率类的对象,放到列表中 # 每次取出一个频率类的对象,执行allow_request方法,如果是False,频率超了,不能再走了 # 如果是True,没有超频率,可以直接往后 for throttle in self.get_throttles(): 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) # 总结: -我们写的频率类:继承BaseThrottle,重写allow_request,在内部判断,
如果超频了,就返回False,如果没超频率,就返回True
自定义频率类
## 视图类views.py from rest_framework.viewsets import ViewSetMixin from rest_framework.generics import ListAPIView from .models import Book from .serializer import BookSerializer from .permissions import CommonPermission from .throttling import SuperThrottle class BookView(ViewSetMixin,ListAPIView): queryset = Book.objects,all() serializer_class = BookSerializer authentication_classes = [] permission_classes = [] throttle_classes = [SuperThrottle]
## throttling.py class SuperThrottle(BaseThrottle): VISIT_RECORD = {} def __init__(self): self.history = None def allow_request(self, request, view): # 自己写逻辑,判断是否超频 # (1)取出访问者ip 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, ] return True # self.history = [时间1] self.history = self.VISIT_RECORD.get(ip,[]) # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间, while self.history and ctime - self.history[-1] > 60: self.history.pop() # (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])
SimpleRateThrottle
# 写一个频率类,重写allow_request方法,在里面实现频率控制 # 1.SimpleRateThrottle---》allow_request源码 def allow_request(self, request, view): # 这里就是通过配置文件和scop取出 频率限制是多少,比如一分钟访问5此 # self.rate是属性,所有应该去__init__内找 >>>:(2) if self.rate is None: return True # 返回了ip,就以ip做限制 self.key = self.get_cache_key(request, view) if self.key is None: return True # 下面的逻辑,跟咱们写的一样 self.history = self.cache.get(self.key, []) self.now = self.timer() while self.history and self.history[-1] <= self.now - self.duration: self.history.pop() if len(self.history) >= self.num_requests: return self.throttle_failure() return self.throttle_success() # 2.SimpleRateThrottle的init方法 def __init__(self): if not getattr(self, 'rate', None): # self.rate= '5、h' # 2.读self.get_rate() >>>:(3) self.rate = self.get_rate() # 5 36000 # 4.读self.parse_rate() self.num_requests, self.duration = self.parse_rate(self.rate) # 3.SimpleRateThrottle的get_rate() 方法 def get_rate(self): if not getattr(self, 'scope', None): msg = ("You must set either `.scope` or `.rate` for '%s' throttle" % self.__class__.__name__) raise ImproperlyConfigured(msg) try: # self.scope 是 lqz 字符串 # return '5/h' return self.THROTTLE_RATES[self.scope] except KeyError: msg = "No default throttle rate set for '%s' scope" % self.scope raise ImproperlyConfigured(msg) # 4.SimpleRateThrottle的parse_rate 方法 def parse_rate(self, rate): # rate是'5/h' if rate is None: return (None, None) # num =5 period= 'hour' num, period = rate.split('/') # num_requests=5 num_requests = int(num) duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] # (5,36000) return (num_requests, duration)
基于APIView编写分页
# 分页功能 只有查询所有才有 from rest_framework.views import APIView from rest_framework.response import Response from .page import CommonPageNumberPagination,CommonLimitOffsetPagination,CommonCursorPagination # 查询所有 class BookView(ViewSetMixin, APIView): def list(self, request): books = Book.objects.all() # 使用步骤 # 1.实例化得到一个分页类的对象 paginator = CommonLimitOffsetPagination() # 2.调用分页类对象的paginate_queryset方法来完成分页,返回的page是要序列化的数据,分页 page = paginator.paginate_queryset(books, request, self) if page is not None: serializer = BookSerializer(instance=page, many=True) # 3 返回数据,调用paginator的get_paginated_response方法 # return paginator.get_paginated_response(serializer.data) return Response({ 'total': paginator.count, 'next': paginator.get_next_link(), 'results': serializer.data })
异常处理
# 1. APIView ------》dispatch----》三大认证,视图类的方法,如果出了异常,就会被异常捕获,捕获后统一处理 # 2.drf内置了一个函数,只要上面过程出了异常,就会执行这个函数,这个函数只处理drf的异常 - 主动抛的非drf异常 - 程序出错了 """ 都不会被处理, 我们的目标,无论主动抛还是程序运行出错,都同意返回规定格式----》能记录日志 """ - 公司里一般返回 {code:100,'msg':'系统错误,请联系系统管理员'} # 3.写一个函数,内部处理异常 在配置文件中配置一下即可 def common_exception_handler(exc,context): # exc 错误对象 # context: 上下文,有view:当前出错的视图类的对象,args和kwargs视图类方法分组出来的参数,request:当次请求的request对象 # 只要走到这里 就要记录日志,只有出错了,才会执行这个函数 # 记录日志尽量详细 print('时间time.time,登录用户id:request.user,用户ip:request.META.get(''),请求方式methods,请求地址,执行的视图类,错误原因') res = exception_handler(exc, context) if res: # 有值,说明返回了Response 对象,没有值说明返回None # 如果是Response 对象说明是drf的异常,已经被处理了,如果是None表明没有处理,就是非drf的异常 res = Response(data={'code': 888, 'msg': res.data.get('detail', '请联系系统管理员')}) else: # res = Response(data={'code': 999, 'msg': str(exc)}) # 记录日志 res = Response(data={'code': 999, 'msg': '系统错误,请联系系统管理员'}) return res
标签:None,return,仙界,get,self,request,rate,08,drf From: https://www.cnblogs.com/juzijunjun/p/17101842.html