Django框架之drf:8、断点调试,权限、认证、频率组件源码分析,基于APIView编写分页,全局异常处理

Django框架之drf

一、断点调式使用

​ 指,在我们编写代码的时候,程序运行出现报错是无可避免的,当程序

出现报错时,我们需要找到出现报错的代码进行修改,如果时简短的代码很容易就可以找到报错位置,但是当代码编写的非常多的时候,报错位置就比较难找到,debug模式就是pycharm为我们提供的查找代码错误位置的功能

1、断点调试

​ 断点调试,英文 breakpoint。用大白话来解释下,断点调试其实就是在debug模式下程序运行的过程中,你在代码某一处打上了断点,当程序跑到你设置的断点位置处,则会中断下来,此时你可以看到之前运行过的所有程序变量。

Django框架之drf:8、断点调试,权限、认证、频率组件源码分析,基于APIView编写分页,全局异常处理

设置完断点后,开启 debug 调试模式运行下,看到结果:
Django框架之drf:8、断点调试,权限、认证、频率组件源码分析,基于APIView编写分页,全局异常处理

Django框架之drf:8、断点调试,权限、认证、频率组件源码分析,基于APIView编写分页,全局异常处理

Django框架之drf:8、断点调试,权限、认证、频率组件源码分析,基于APIView编写分页,全局异常处理

Django框架之drf:8、断点调试,权限、认证、频率组件源码分析,基于APIView编写分页,全局异常处理

二、权限组件源码分析

# 权限的源码执行流程 	-写一个权限类,局部使用,配置在需要使用的视图类下,就会执行权限类的has_permission方法,完成权限校验   # 之前读过: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的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 

1、自定义频率类

class MySuperThrottle(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,表示第一次访问,在字典里,继续往下走 {ip地址:[时间1,时间2,时间3,时间4]}         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,顺利通过         if len(self.history) < 3:             self.history.insert(0, ctime)             return True         # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败         else:             return False 

2、SimpleRateThrottle

# 写一个频率类,重写allow_request方法,在里面实现频率控制 def my_SimpleRateThrottle(SimpleRateThrottle):     def allow_request(self, request, view):         # 这里就是通过配置文件和scop取出 频率限制是多少,比如一分钟访问5此         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()          # SimpleRateThrottle的init方法      def __init__(self):         if not getattr(self, 'rate', None):             # self.rate= '5、h'             self.rate = self.get_rate()         # 5    36000         self.num_requests, self.duration = self.parse_rate(self.rate)         # 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)          #     SimpleRateThrottle的parse_rate 方法      def parse_rate(self, 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编写分页

# 分页功能,只有查询所有才有   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(),                 'previous': paginator.get_previous_link(),                 'results': serializer.data             }) 

六、异常处理

# APIView--->dispatch--->三大认证,视图类的方法,如果出了异常,就会被捕获,捕获后统一处理  # drf 内置了一个函数,只要上面过程出了异常,就会执行这个函数,这个函数只处理的drf的异常     -主动抛的非drf异常     -程序出错了 :     都不会被处理     我们的目标,无论主动抛还是程序运行出错,都统一返回规定格式--》能记录日志 公司里一般返回   {code:999,'msg':'系统错误,请联系系统管理员'}   # 写一个函数,内部处理异常,在配置文件中配置一下即可     def common_exception_handler(exc, context):         # exc 错误对象         # context:上下文,有view:当前出错的视图类的对象,args和kwargs视图类方法分组出来的参数,request:当次请求的request对象         # 只要走到这里,就要记录日志 ,只有错了,才会执行这个函数         # 记录日志尽量详细         print('时间,登录用户id,用户ip,请求方式,请求地址,执行的视图类,错误原因')         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      # 在配置文件中配置 REST_FRAMEWORK = { 	'EXCEPTION_HANDLER': 'app01.exceptions.common_exception_handler', } 

发表评论

相关文章