一、自定义频率类
# 我们之前写的频率类其实是可以继承两个的SimpleRateThrottle, BaseThrottle # 只不过现继承BaseThrottle需要重写BaseThrottle方法 我们现在按照继承BaseThrottle然后自定义频率 class MyThrottling(BaseThrottle): # 自定义的逻辑 {ip:[时间1,时间2]} time_dic = {} def __init__(self): self.history = None def allow_request(self, request, view): pass # (1)取出访问者ip ip = request.META.get('REMOTE_ADDR') # (2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走 import time now_time = time.time() if ip not in self.time_dic: self.time_dic[ip] = [now_time, ] return True self.history = self.time_dic.get(ip) # (3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间, while self.history and now_time - self.history[-1] > 60: self.history.pop() # (4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过 if len(self.history) < 3: self.history.insert(0, now_time) return True # (5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败 else: return False # 然后在视图类中配置即可 但是现在错误信息不是中文的 需要重写wait方法才能显示中文 def wait(self): import time now_time = time.time() return 60 - (now_time - self.history[-1]) # 用当前时间减去列表中最后一个时间 就是剩余时间
二、频率类源码分析
# 我们在继承了SimpleRateThrottle类 然后在写频率类 其实就是这个帮我们写了allow_request方法所以我们去看看这个源码写了啥 def allow_request(self, request, view): if self.rate is None: # 首先判断了self.rate是否有值 rate在实例化对象的时候就已经赋值了 所以肯定有值 return True self.key = self.get_cache_key(request, view) # get_cache_key就是我们自己写的频率类重新写的方法 然后我们返回了用户的ip作为频率的限制 所以我们返回什么就以什么作为频率的限制 if self.key is None: # 所以这里为True做if判断 return True # 这里其实就是没有频率的限制 直接往下走 self.history = self.cache.get(self.key, []) # 通过缓存取出列表中用户ip的时间 self.now = self.timer() # 这个就是获取当前时间 self.timer()其实就是time.time while self.history and self.history[-1] <= self.now - self.duration: # 然后循环判断列表中是否有值, self.history就是列表最后一个时间就是最早的时间 # self.now就是当前时间 self.duration 就是我们配置的60 # 然后如果最早的时间如果小于 当前时间减去我们配置的时间那么 就要把最早的时间给pop掉 # 这样列表中就只剩下小于我们配置的时间 self.history.pop() if len(self.history) >= self.num_requests: # 然后在计算列表中小于我们配置的时间 是否大于我们配置的频率次数 # 如果大于那么就要返回False return self.throttle_failure() # 这里点进去其实就是返回了False 为了扩展性 # 否则返回True return self.throttle_success() def throttle_success(self): self.history.insert(0, self.now) # 这个就是把当前时间插入到列表的开头 self.cache.set(self.key, self.history, self.duration) # 然后保存到缓存当中 return True # 返回True 就是没有限制
SimpleRateThrottle类
class SimpleRateThrottle(BaseThrottle): timer = time.time THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES def __init__(self): if not getattr(self, 'rate', None): # 这里就是使用反射判断我们写的类中是否有rate方法 我们肯定没写 所以if判断为True 就会走这个判断 self.rate = self.get_rate() # 然后self.rate有等于了self.get_rate() 所以我们根据查找顺序 要找到了该类下的get_rate方法 # 然后该方法返回了'3/m' self.num_requests, self.duration = self.parse_rate(self.rate) # 先执行了self.parse_rate方法 根据名字的查找顺序 最后还是在该类中找的该方法 然后该方法返回了3和60 # 所以self.num_requests等于3 self.duration等于60 # 这样就实例化完了
get_rate方法
def get_rate(self): if not getattr(self, 'scope', None): # 利用反射判断我们写的频率类中是否有scope方法或属性 我们是写的了所以if结果为False 所以不会走该if msg = ("You must set either `.scope` or `.rate` for '%s' throttle" % self.__class__.__name__) raise ImproperlyConfigured(msg) # 如果没写就会抛异常 所以一定要写scope属性 try: return self.THROTTLE_RATES[self.scope] # self.scope就是我们自己写的频率类中写的scope属性'luffy' # THROTTLE_RATES根据上面的赋值其实就是这个就是我们在配置文件中配置的'DEFAULT_THROTTLE_RATES': {'luffy': '3/m'}字典 # 然后根据字典取值把'3/m'返回出去 except KeyError: msg = "No default throttle rate set for '%s' scope" % self.scope raise ImproperlyConfigured(msg)
parse_rate
def parse_rate(self, rate): # rate就是传进来的 '3/m' if rate is None: # 所以rate肯定有值 return (None, None) # 没有就返回两个None num, period = rate.split('/') # 然后根据 / 切割字符串'3/m' # 然后有通过解压赋值 num=字符串的3, period=m num_requests = int(num) # 将字符串的3转换为整型3 赋值给num_requests duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] # period[0] 就是取出字符串索引为0的数据 那就是m # 这个就是 duration = {} 然后duration['m'] 是一样的 # 然后通过字典的取值方法 取出k为m的Value 所以duration就是60 return (num_requests, duration) # 然后把3和60返回出去
总结
# 总结:以后要再写频率类,只需要继承SimpleRateThrottle,重写get_cache_key,配置类属性scope,配置文件中配置一下就可以了
三、分页功能
# 分页功能就只有获取全部的接口才会有分页,其他接口是没有分页功能的 # 然后后端的分页写法是固定的,只有前端展示的不一样而已 eg: -pc端的上一页和下一页的点击 -app的上拉和下滑都是分页的功能 # drf的分页使用 -所以写一个类,然后继承drf提供的三个分页类之一 -然后配置属性 -然后在视图类中,视图类必须要继承GenericAPIView+ListModelMixin的子视图类上 因为只有GenericAPIView类有pagination_class属性 -如果继承的是APIView,需要自己写 page = MyPageNumberPagination() res = page.paginate_queryset(qs, request)
代码演示
1.PageNumberPagination
# 自己写的分页类 class MyPageNumberPagination(PageNumberPagination): page_size = 2 # 每页展示2条数据 page_query_param = 'page' # 路径按照这样传入 books/?page=1 查询第几页 page_size_query_param = 'size' # 路径还可以这样写 books/?page=3&size=4 查询第三页,该页显示4条数据 max_page_size = 5 # size后面如果超出5 那么该页就按照5条数据显示,没有超出就按照写的条数显示 # 视图类配置 class BooksView(ViewSetMixin, GenericAPIView, ListAPIView): queryset = Book.objects.all() serializer_class = BookSerializer pagination_class = MyPageNumberPagination # 在该类属性配置即可 # 然后通过 http://127.0.0.1:8000/books/?page=1路径即可访问第一页的数据
2.LimitOffsetPagination
class MyLimitOffsetPagination(LimitOffsetPagination): default_limit = 2 # 每页显示的条数 limit_query_param = 'limit' # 路径按照这样传入 books/?limit=4 这一页显示4条数据 offset_query_param = 'offset' # 路径还可以这样写 books/?offset=3&limit=4 # 我从第三条开始,然后取四条数据 max_limit = 5 # limit后面如果超出5 那么该页就按照5条数据显示,没有超出就按照写的条数显示 # 视图类 class BooksView(ViewSetMixin, GenericAPIView, ListModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer pagination_class = MyLimitOffsetPagination # 因为分页只能是一种方式 所以只需要替换即可 # 然后就可以根据这个路径访问了 http://127.0.0.1:8000/books/?offset=3&limit=3
3.CursorPagination
class MyCursorPagination(CursorPagination): cursor_query_param = 'cursor' page_size = 2 # 每页显示的条数 ordering = 'id' # 按照表中的某个字段排序 我们按照id排序 加负号就是降序 # 试图类 class BooksView(ViewSetMixin, GenericAPIView, ListModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer pagination_class = MyCursorPagination # 这种分页方式 虽然不能跳到某一页 但是这种是效率最高的 适合大数据分页
四、排序功能
# 排序肯定也是 只有获取全部的接口需要排序 # 排序功能drf提供了给我们有排序类 from rest_framework.filters import OrderingFilter
使用
class BooksView(ViewSetMixin, GenericAPIView, ListModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer pagination_class = MyCursorPagination # 排序类的配置 filter_backends = [OrderingFilter, ] # 可以放多个 # 指定那个字段排序 ordering_fields = ['id', 'price'] # 按照id排序也可以按照price排序 # 排序还可以跟分页一起使用 但是是先排序后分页
五、过滤功能
# 过滤功能也是一样的 只有获取全部需要过滤 其他接口不需要过滤 # restful规范中有一条,请求地址中带过滤条件:分页,排序,过滤统称为过滤 # 使用内置过滤类使用步骤 查询所有才涉及到排序,其它接口都不需要 # 必须是继承GenericAPIView+ListModelMixin的子视图类上
使用
from rest_framework.filters import SearchFilter class BooksView(ViewSetMixin, GenericAPIView, ListModelMixin): queryset = Book.objects.all() serializer_class = BookSerializer # 过滤类的配置 filter_backends = [SearchFilter, ] # 这个因为是列表 所以可以跟排序一起使用 ,因为排序和过滤的类属性是一起的 # 指定那个字段过滤 search_fields = ['title'] # 使用那个字段过滤 # 过滤也是可以跟分页一起使用的 只不过先过滤在分页
标签:分页,self,history,rate,过滤,time,排序,class,drf From: https://www.cnblogs.com/stephenwzh/p/16777374.html