首页 > 其他分享 >drf从入门到飞升仙界 08

drf从入门到飞升仙界 08

时间:2023-02-19 12:55:29浏览次数:42  
标签:None return 仙界 get self request rate 08 drf

断点调试使用

# 程序在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

相关文章

  • day08-String和ArrayList
    1,基本介绍介绍两个API:String类和ArrayList类API(全称ApplicationProgrammingInterface:应用程序编程接口)就是别人写好的一些程序,给程序员直接拿去调用即可解决问题的......
  • 代码随想录算法训练营day23 | leetcode 669. 修剪二叉搜索树 ● 108.将有序数组转换
    LeetCode669.修剪二叉搜索树分析1.0递归遍历树时删除符合条件(不在区间中)的节点-如何遍历如何删除如果当前节点大于范围,递归左树,反之右树当前节点不在范围内,删除它,把......
  • [oeasy]python0086_ASCII_出现背景_1963年_DEC_PDP系列主机_VT系列终端
    编码进化回忆上次内容上次回顾了字符编码的新陈代谢ibm曾经的EBCDIC由于字符不连续导致后续出现无数问题随着网络的发展数据交换的需要原来的小隐患现在产生了......
  • [oeasy]python0086_ASCII_出现背景_1963年_DEC_PDP系列主机_VT系列终端
    编码进化回忆上次内容上次回顾了字符编码的新陈代谢ibm曾经的EBCDIC由于字符不连续导致后续出现无数问题随着网络的发展数据交换的需要原来的......
  • pat1008
    这个还是比较简单的1#include<iostream>2#include<stdio.h>3usingnamespacestd;45intmain()6{7intn,m;8scanf("%d%d",&n,&m);9......
  • PAT-basic-1008 数组元素循环右移问题 java
    一、题目一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由(A0A1⋯AN−1)变换为(AN−M⋯AN−1A0A1⋯AN−M−1)(最后M个数......
  • linux驱动移植-I2C驱动移植(OLED SSD1306/AT24C08)
    ----------------------------------------------------------------------------------------------------------------------------内核版本:linux5.2.8根文件系统:busybo......
  • 牛客练习赛108-B
    原题描述:链接:https://ac.nowcoder.com/acm/contest/51208/B原题题解:链接:https://ac.nowcoder.com/discuss/1121134题意:给出长度为n的正整数序列a,b,可以选择相邻的两个数......
  • Power BI 刷新数据集,提示报错:Return records size cannot exceed 83886080
    错误效果:  错误原因:Dataverse链接方式,有80M的容量限制处理办法:更换PowerBI里面的数据源链接方式1、使用官方连接方式加载具体连接方式参照官网文档:将PowerB......
  • P1908 逆序对
    题目描述猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的......