def 序列化组件,,请求和响应__视图__路由组件__三大认证,,排序,过滤吗,全局异常,分页,,频率源码分析

前情回顾:

drf入门规范

-前后端开发模式:混合开发(不分离),前后端分离
    -API接口
    -接口测试:postman
        -测试:接口测试---》jmeter:java开发的软件
    -restful规范:10
    -drf 帮助我们快速实现符合restful规范的接口
    -APIView执行流程--》重写了as_view--》重写了dispatch
        -1 去除了csrf认证
        -2 包装了新的request
        -3 三大认证  ---》根据请求方式,执行视图类的方法
        -4 处理了全局异常
    -Request 类的对象
        -1 多了data
        -2 重写了 __getattr__  对象.属性 触发
            -去旧的request中找:通过反射  getattr(self._request,'path')
        -3 以后用起来跟之前一样
        -4 query_params

 

序列化组件

  

-作用:
        1 序列化
            -1 写一个类,继承(序列化类)
            -2 写字段,需要跟表中有对应关系(手动对应:Serializer 自动对应:ModelSerializer)
            -3 视图类中使用
                -多条Queryset对象:使用orm查询出来的
                -django执行原生sql---》[book1,book2]-->使用序列化类完成序列化
                -ser=BookSerializer(instnce=qs,many=True)
                -ser.data+Response
        2 反序列化
            -1 写一个类,继承(序列化类)
            -2 写字段,需要跟表中有对应关系(手动对应:Serializer 自动对应:ModelSerializer)
            -3 视图类中使用
                -修改ser=BookSerializer(instance=对象,data=request.data)
                -新增ser=BookSerializer(data=request.data)
                -ser.is_valid()
                -ser.save()--->触发序列化类的:update,create
                
        3 序列化和反序列化用在一起
            -read_only 和 write_only
            -extra_kwargs={}
        3 校验
            -三层
      
    -Serializer
        -重写 update和create
    -ModelSerializer
        -重写字段
        -class Meta:
            model 
            fields 
            extra_kwargs
      
   -定制返回格式:序列化
        -source定制:表中字段,表中方法,跨表
        -表模型中写方法
        -序列化类中:SerializerMethod--》get_字段名
    
    
    
   
# BookReadSerializer
# BookWriteSerializer
# LoginSerializer
class BookView(ModelViewSet):
    query_set=xx
    serializer_class=BookReadSerializer
    # 重写 get_serializer_class 方法
    def get_serializer_class(self):
        # 影响:list  retrieve    update create    自己写的
        if self.action=='list' or self.action=='retrieve':
        # if self.request.method=='get':
            # get_serializer() 获取使用哪个序列化类
            return BookReadSerializer
        elif self.action=='login':
            return LoginSerializer
        else:
            return BookWriteSerializer
        
    def get_queryset(self):
        # list  retrieve     update create   delete
                get_object
        # 会影响到谁? list,retrieve,update,delete   
        # 不会影响到create
        
        返回所有数据,但会影响 list,retrieve,update,delete ,自己写的
    @action(methods=['POST'],detail=False)
    def login(self,requset):
        self.get_serializer() #让它拿到 LoginSerializer
        self.get_object()
        self.get_queryset
        

请求和响应

-请求:
        -Request源码
        -能够解析的编码格式:默认三种:json,urlencoded,form-data
    -响应:
        -Response源码
            -实例化的时候,可以传入的参数
                data=None,   响应体中
                status=None, 响应状态码
                headers=None, 响应头
             -list方法---》res=super().list(request)--->res 就是Response 类的对象
             -res.status  res.data  res.headers
             -res.data   [{},{},{}]
             -{code:100,msg:成功,results:res.data}
        -响应编码格式        
         
# 4 视图组件
    -APIView
        -1 继承了View
        -2 重写了 as_view
        -3 重写了 dipatch---》相当于给之前的视图类的方法加了装饰器
        -4 写跟请求方式同名的方法:get,post,delete,put,get
    -GenericAPIView
        -1 继承了APIView
        -2 类属性
            queryset =Book.object.all().filter(is_delete=False)
                # 要序列化的数据---》self.get_queryset拿到--》只要方法中使用了get_queryset,就会受它影响
            serializer_class 
                # 序列化类:list,create,put,retrieve,自己写的方法
                #通过重写 get_serializer_class控制视图类的方法使用哪个序列化类
            lookup_field = 'pk' 
                # 分组,转换器出来的参数
            filter_backends # 过滤和排序 
            pagination_class #分页
       -3 方法
         - get_serializer_class---》用来重写
         - serializer_class---》获取序列化类---》list,retrieve(get_object间接),put,create
         - get_queryset--->要序列化的总数据或 单条查询的数据源
         - get_object --》获取单条---》通过get_queryset拿到的
         - filter_queryset--》过滤使用---》一般不重写--》必须配合list使用
            -视图类中重写它--》可以完成过滤--》就不用配置过滤类了
            
         -get_paginated_response --》获取分页后的返回数据--》必须配合list
            -通过重写它,定制分页后的返回数据格式
            -def get_paginated_response(self, data):
                assert self.paginator is not None
                return Response({
                    'code': 100,
                    'msg': 'asdfasfasfdasf',
                    'next': self.paginator.get_next_link(),
                    'pre': self.paginator.get_previous_link(),
                    'result': data})
                
      -5个视图扩展类--》必须搭配GenericAPIView
        -ListModelMixin
            # 过滤,排序,分页
            def list(self, request, *args, **kwargs):
                # 走了过滤和排序
                queryset = self.filter_queryset(self.get_queryset())
                # 处理了分页
                page = self.paginate_queryset(queryset)
                if page is not None:
                    serializer = self.get_serializer(page, many=True)
                    return self.get_paginated_response(serializer.data)
              # 如果没有分页,正常返回
                serializer = self.get_serializer(queryset, many=True)
                return Response(data=serializer.data)
        -RetrieveModelMixin
            def retrieve(self, request, *args, **kwargs):
                instance = self.get_object() # 序列化单挑数据--》重写
                serializer = self.get_serializer(instance) # 重写--》使用哪个序列化类
                return Response(serializer.data)
        -CreateModelMixin
            def create(self, request, *args, **kwargs):
                serializer = self.get_serializer(data=request.data) # 重写
                serializer.is_valid(raise_exception=True)
                self.perform_create(serializer) #做了真正的保存serializer.save(),重写
                return Response(serializer.data, status=status.HTTP_201_CREATED)

            def perform_create(self, serializer):  #可以重写
                serializer.save()
                
        -DestroyModelMixin
            def destroy(self, request, *args, **kwargs):
                instance = self.get_object() # 通过重写它,决定删除谁
                self.perform_destroy(instance) 
                # 重写--》软删除-》不在数据库删除-》is_delete
                # 设置为True,以后要序列化的时候,不查出来了
                return Response(status=status.HTTP_204_NO_CONTENT)

            def perform_destroy(self, instance):
                instance.delete()

        -UpdateModelMixin
            def update(self, request, *args, **kwargs):
                instance = self.get_object()
                serializer = self.get_serializer(instance, data=request.data)
                serializer.is_valid(raise_exception=True)
                self.perform_update(serializer)
                return Response(serializer.data)

            def perform_update(self, serializer):
                serializer.save()
                
                
     -9 个视图子类-->重写 get_serializer_class,get_queryset,get_object
        ListAPIView:GenericAPIView+ListModelMixin--》重写:list,get
            写了个方法:get---》核心就是--》self.list
        RetrieveAPIView--》重写retrieve,get
            写了个方法:get---》核心就是--》self.retrieve
        CreateAPIView-->重写create,perform_create
            写了个方法:post---》核心就是--》self.create
        UpdateAPIView
        DestroyAPIView
        ---------
        ListCreateAPIView
        ---------
        RetrieveUpdateDestroyAPIView,
        RetrieveDestroyAPIView,
        RetrieveUpdateAPIView

视图集 

-ViewSetMixin  # 路由写法变了---》多个视图类可以放到一起---》一个视图类中可以放多个方法
        -GenericViewSet
         -ViewSet
        -ModelViewSet:有get,post这些方法吗?没有,只有list,create。。这些
            -重写:get_serializer_class,get_queryset,perform_destroy
            -重写list,分页返回格式
        -ReadOnlyModelViewSet:只有 list,retrieve
    
  -以后写视图类: 一般不会5个都写
    -比如只想写 查询所有和删除一条--继承--》GenericViewSet,ListModelMixin,DestroyModelMixin

路由组件

- 映射写法:ViewSetMixin
    - 自动生成路由:ViewSetMixin---》用的多
    
    - action装饰器:methods,detail
    - SimperRouter和DefaultRouter

三大认证

-认证类的使用
    -权限类:
        -登录成功后,有没有权限---》request.user拿到登录用户,判断权限
    -频率类:
        ip,用户id
        -用户访问记录:存在缓存中,默认在内存

排序,过滤,全局异常

-排序:内置排序  list接口,继承GenericAPIView+list
        -filter_backends 配置 排序类
        -ordering=id,-price
    -过滤:
        -内置
        -第三方
        -自定义:写个类,继承:BaseFilterBackend,重写filter_queryset在内部完成过滤,返回过滤后的qs对象
        
    -过滤,排序可以连用,多个过滤也可以连用
    
    -全局异常:
        -写个函数,完成处理,配置到配置文件---》以后只要出了异常,都会走咱么的函数

 分页

- list接口+GenericAPIView 配置分页类
    - 三种分页方式
        -都有自己的类属性
        
    -继承APIView实现分页
    from rest_framework.viewsets import ViewSet
    from rest_framework.mixins import ListModelMixin,RetrieveModelMixin
    from rest_framework.generics import GenericAPIView
    from . import pagination
    class BookView(ViewSet):
        def list(self, request):
            book_list = Book.objects.all()
            # 调用咱们写的分页类对象的paginate_queryset方法返回了 分页后的qs对象
            # pagenation = pagination.CommonPageNumberPagination()
            # pagenation = pagination.CommonLimitOffsetPagination()
            pagenation = pagination.CommonCursorPagination()
            page = pagenation.paginate_queryset(book_list, request, self)
            ser = BookSerializer(instance=page, many=True)
            # return pagenation.get_paginated_response(ser.data)
            # CommonPageNumberPagination 返回格式
            # return Response({'code': 100,
            #                  'msg': '查询成功',
            #                  'count': pagenation.page.paginator.count,
            #                  'next': pagenation.get_next_link(),
            #                  'pre': pagenation.get_previous_link(),
            #                  'result': ser.data})
            # CommonLimitOffsetPagination
            # return Response({
            #     'code': 100,
            #     'msg': '查询成功',
            #     'count': pagenation.count,
            #     'next': pagenation.get_next_link(),
            #     'pre': pagenation.get_previous_link(),
            #     'result': ser.data})

            # CommonCursorPagination
            return Response({
                'code': 100,
                'msg': '查询成功',
                'next': pagenation.get_next_link(),
                'pre': pagenation.get_previous_link(),
                'result': ser.data})

----------------------------------------------------------------------------------------------------------------------

频率源码分析

  自定义频率类

      

 复杂方案---》通用方案---》后期任何框架都可以使用这个逻辑
from rest_framework.throttling import BaseThrottle
import time


class MyThrottling(BaseThrottle):
    # 后来的时间,放到第一个位置
    VISIT_RECORD = {}  # 以后,访问者ip放在这个字典中  {192.168.1.11:[时间2,时间1],192.168.1.99:[时间3,时间2,时间1]}

    def __init__(self):
        self.history = None

    def allow_request(self, request, view):
        # 在这里,实现 频率限制
        # (1)取出访问者ip
        ip = request.META.get('REMOTE_ADDR')
        # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
        ctime = time.time()
        if ip not in self.VISIT_RECORD:
            self.VISIT_RECORD[ip] = [ctime, ]
            return True
        # (3)如果当前的ip,在访问列表中,循环判断当前ip的访问时间列表列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
        self.history = self.VISIT_RECORD.get(ip)  # 访问时间列表
        # ctime - self.history[-1]  访问了最久的时间,如果大于60,说明 一分钟之前访问过的,这个时间是无效的,pop掉
        while self.history and ctime - self.history[-1] > 60:
            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])

频率源码执行流程

# 1 APIView--->dispatch--->self.initial(request, *args, **kwargs)--->self.check_throttles(request)
# 2 APIView的check_throttles
    def check_throttles(self, request):
        throttle_durations = []
        # self.get_throttles() 是视图类中配置的一个个频率类的对象列表
        for throttle in self.get_throttles():
            # throttle.allow_request 返回false ,说明到频率了
            if not throttle.allow_request(request, self):
                # 到了频率,走了它
                # throttle.wait() 返回还剩多长时间能访问
                throttle_durations.append(throttle.wait())
        # 被频率限制住了,它就有值
        if throttle_durations:
            durations = [
                duration for duration in throttle_durations
                if duration is not None
            ]
           #duration=[35,54]
            # duration=[]
            duration = max(durations, default=None)
            # duration=56
            # duration=None
            self.throttled(request, duration)
            
            
            
# 3 self.throttled(request, duration) ---》APIView
    def throttled(self, request, wait):
        # wait=56或None
        raise exceptions.Throttled(wait)
        
# 4 Throttled类实例化得到对象,传了数字进去
from rest_framework.exceptions import Throttled
内部 拼接错误信息--》但是是英文的,如果要换成中文
# 5 超过了频率,就不返回False了,直接抛异常
    raise Throttled(None, '超过了限制,还剩:%s 时间' % self.wait())

SimpleRateThrottle 执行流程

 1 咱们写的  CommonThrottling没有写allow_request
class CommonThrottling(SimpleRateThrottle):
    rate = '3/m'
    def get_cache_key(self, request, view):
        return request.META.get('REMOTE_ADDR')
    
    
# 2 一定是 SimpleRateThrottle 写了,完成了频率校验
# 3 SimpleRateThrottle--》allow_request---》跟咱们的逻辑一样
    def allow_request(self, request, view):
        if self.rate is None: # 自己写了 '3/m'
            return True
    
        # 返回了 ip地址,self.key 就是访问者的ip地址
        self.key = self.get_cache_key(request, view)
        if self.key is None:
            return True

        # self.history 当前ip,访问的时间列表,self.key是ip
        # self.cache 缓存,去缓中,根据ip,取出访问者时间列表,如果没有就是 []
        self.history = self.cache.get(self.key, [])
        # 当前时间
        self.now = self.timer() # timer = time.time
       #  self.now 当前时间,
       #  self.duration 就是60
        while self.history and self.history[-1] <= self.now - self.duration:
            self.history.pop()
        # self.num_requests 是3
        if len(self.history) >= self.num_requests:
            return self.throttle_failure()
        return self.throttle_success()
    
    
# 4  self.duration 就是60    self.num_requests 是3
# 3                   60
self.num_requests, self.duration = self.parse_rate(self.rate)
#5 self.parse_rate(self.rate)
    def parse_rate(self, rate):
        if rate is None: # '3/minute'
            return (None, None)
        # 3   period=mmmmm
        num, period = rate.split('/')
        # 3
        num_requests = int(num)
        duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
        # duration 是60,
        # num_requests 是3
        return (num_requests, duration)

 

posted @ 2024-01-02 15:09  拆尼斯、帕丁顿  阅读(26)  评论(0)    收藏  举报