内容概览
- 自定义频率类
- 频率源码分析
- 分页、排序、过滤
自定义频率类
class Throttle_app01(BaseThrottle):
scope = 'scope'
VISIT_RECORD = {} # 存放用户访问记录:{判断依据:[访问时间1,访问时间2]}
def __init__(self):
self.history = None # 保存时间列表
def allow_request(self, request, view):
ip = request.META.get('REMOTE_ADDR')
import time
ctime = time.time() # 获取当前时间
if ip not in self.VISIT_RECORD: # 判断当前用户ip是否在访问字典中
self.VISIT_RECORD[ip] = [ctime] # 如果不存在说明当前ip是第一次访问,在访问字典中新建一条数据并返回True
return True
self.history = self.VISIT_RECORD.get(ip) # 如果当前用户ip访问过,获取当前ip的访问时间列表
while self.history and ctime - self.history[-1] > 60:
self.history.pop() # 如果列表中有值且最早登录时间大于限定时间,则删除该时间
if len(self.history) < 3:
self.history.insert(0, ctime)
return True # 如果时间列表中个数小于限定次数,则将当前时间插入到第一条,并返回True
else:
return False # 如果时间列表中个数大于限定次数,则不通过验证返回False
def wait(self):
ctime = time.time()
return 60 - (ctime-self.history[-1]) # 返回最早登录时间与限定时间之间相差多少时间
频率源码分析
# SimpleRateThrottle
-源码里执行的频率类的allow_request,读SimpleRateThrottle的allow_request
class SimpleRateThrottle(BaseThrottle):
cache = default_cache
timer = time.time
cache_format = 'throttle_%(scope)s_%(ident)s'
scope = None
THROTTLE_RATES = api_settings.DEFAULT_THROTTLE_RATES
def __init__(self): # 只要类实例化得到对象就会执行
if not getattr(self, 'rate', None): # 去频率类中反射rate属性或方法,发现没有,返回了None,这个if判断就符合,执行下面的代码
self.rate = self.get_rate() #执行get_rete方法,返回了 '3/m'
self.num_requests, self.duration = self.parse_rate(self.rate) # 执行parse_rate方法,传入字典中填入的3/m;返回结果是一个元组
def get_rate(self):
return self.THROTTLE_RATES[self.scope] # 字典取值,配置文件中咱们配置的字典{'ss': '3/m',},根据ss取到了 '3/m'
def parse_rate(self, rate):
if rate is None:
return (None, None)
num, period = rate.split('/') # 根据/切割并解压赋值
# num = '3', period = 'm'
num_requests = int(num) # 将字符串'3'转换为整型
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] # 先通过字符串索引取值,取到'm';在通过字典的按key取值,取到60
return (num_requests, duration)
def allow_request(self, request, view):
if self.rate is None: # 经过__init__可以获取到self.rate的值,如果为None则不校验直接通过
return True
self.key = self.get_cache_key(request, view) # 咱们自己写的,返回什么就以什么做限制 咱们返回的是ip地址
if self.key is None: # self.key=当前访问者的ip地址
return True
self.history = self.cache.get(self.key, []) # self.history 访问者的时间列表,从缓存中拿到,如果拿不到就是空列表,如果之前有 [时间2,时间1]
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() # 方法返回值为False
return self.throttle_success() # 方法返回值为True
# 总结:以后要再写频率类,只需要继承SimpleRateThrottle,重写get_cache_key,配置类属性scope,配置文件中配置一下就可以了
分页、排序、过滤
分页功能
只有查询所有数据的接口才需要使用到分页
分页后端写法是固定的,前端展示形式不一样
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
# 继承这三个类都可以使用分类
class BookPageNumber(PageNumberPagination):
page_size = 5 # 每页默认显示5条数据
page_query_param = 'page' # 页数在路由中过滤条件的名字:/books/?page=3 第三页
page_size_query_param = 'size' # 在路由过滤条件中写入 /books/?size=10 则表示每页展示10条数据
max_page_size = 20 # 表示每页最多展示20条数据
class BookLimitOffset(LimitOffsetPagination):
default_limit = 5 # 默认每页展示数据数量
limit_query_param = 'limit' # /book/?limit=6 表示展示6条数据
offset_query_param = 'offset' # /book/?limit=2&offset=10 表示从第10条开始往后展示2条数据
max_limit = 10 # 表示最多展示10条数据
class BookCursor(CursorPagination):
cursor_query_param = 'cursor' # 路由过滤条件名
page_size = 5 # 每页展示条数
ordering = 'id' # 按照什么排序,必须为表中字段;-id表示降序排序
"""这个类只能够上一页和下一页,不能够直接跳转到指定页数;适用于数量庞大的数据展示"""
"""把它配置到继承了GenericAPIView+ListModelMixin的子视图类属性上"""
class BookView(ModelViewSet):
pagination_class = paginations.BookCursor
排序
查询所有数据的接口才需要使用到排序
继承了GenericAPIView和ListModelMixin的子视图类才能配置
from rest_framework.filters import OrderingFilter
class BookView(ModelViewSet):
filter_backends = [OrderingFilter, ] # 配置排序类
ordering_fields = ['id', 'price'] # 配置排序的字段
"""http://127.0.0.1:8000/books/?ordering=price,id 路由写法,先通过价格排序,价格相同通过id排序,默认为升序排序,加-号表示降序排序"""
过滤
过滤也只有在查询所有接口才需要使用
继承了GenericAPIView和ListModelMixin的子视图类才能配置
from rest_framework.filters import SearchFilter
class BookView(ModelViewSet):
filter_backends = [SearchFilter, ] # 配置过滤类
search_fields = ['name', 'publish'] # 配置过滤的字段
"""http://127.0.0.1:8000/books/?search=三 只要name中或publish中有三都能搜出来
内置过滤类只能通过search写条件"""
练习
"""1 自定义频率类,写一遍"""
class Throttle_app01(BaseThrottle):
scope = 'scope'
VISIT_RECORD = {}
def __init__(self):
self.history = None
def allow_request(self, request, view):
ip = request.META.get('REMOTE_ADDR')
import time
ctime = time.time()
if ip not in self.VISIT_RECORD:
self.VISIT_RECORD[ip] = [ctime]
return True
self.history = self.VISIT_RECORD.get(ip)
while self.history and ctime - self.history[-1] > 60:
self.history.pop()
if len(self.history) < 3:
self.history.insert(0, ctime)
return True
else:
return False
def wait(self):
ctime = time.time()
return 60 - (ctime-self.history[-1])
"""2 使用3种分页方式,实现对查询所有数据接口的分页"""
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
class BookPageNumber(PageNumberPagination):
page_size = 5
page_query_param = 'page'
page_size_query_param = 'size'
max_page_size = 10
class BookLimitOffset(LimitOffsetPagination):
default_limit = 5
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 10
class BookCursor(CursorPagination):
cursor_query_param = 'cursor'
page_size = 5
ordering = 'id'
"""3 带排序,带按名字过滤"""
class BookView(ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = serializers.BookModelSerializer
filter_backends = [OrderingFilter, SearchFilter]
ordering_fields = ['id', 'price']
search_fields = ['name']
class PublishView(ModelViewSet):
queryset = models.Publish.objects.all()
serializer_class = serializers.PublishModelSerializer
filter_backends = [OrderingFilter, SearchFilter]
ordering_fields = ['id', 'city']
search_fields = ['name']