1、限流Throttling
可以对接口访问的频次进行限制,以减轻服务器压力
一般用于付费购买次数,投票等场景使用
1.1、自定义频率类
1.1.1、编写频率类
# 自定义的逻辑
#(1)取出访问者ip
#(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
#(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
#(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
#(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
class MyThrottles():
VISIT_RECORD = {}
def __init__(self):
self.history=None
def allow_request(self,request, view):
#(1)取出访问者ip
# print(request.META)
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=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])
1.1.2、全局使用
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES':['app01.utils.MyThrottles',],
}
1.1.3、局部使用
# 在视图类里使用
throttle_classes = [MyThrottles,]
1.2、内置频率类
1.2.1、根据用户ip限制
# 写一个类,继承自SimpleRateThrottle(根据ip限制)
from rest_framework.throttling import SimpleRateThrottle
class VisitThrottle(SimpleRateThrottle):
scope = 'xxx'
def get_cache_key(self, request, view):
return self.get_ident(request)
#在setting里配置: (一分钟访问三次)
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES':{
'xxx':'3/m' # key要跟类中的scop对应
}
}
# 可以全局使用,局部使用
了解:错误信息中文显示
class Course(APIView):
authentication_classes = [TokenAuth, ]
permission_classes = [UserPermission, ]
throttle_classes = [MyThrottles,]
def get(self, request):
return HttpResponse('get')
def post(self, request):
return HttpResponse('post')
def throttled(self, request, wait):
from rest_framework.exceptions import Throttled
class MyThrottled(Throttled):
default_detail = 'xxx'
extra_detail_singular = '还有 {wait} second.'
extra_detail_plural = '出了 {wait} seconds.'
raise MyThrottled(wait)
1.2.2、限制匿名用户每分钟访问3次
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'anon': '3/m',
}
}
# 使用 `second`, `minute`, `hour` 或`day`来指明周期。
# 可以全局使用,局部使用
1.2.3、限制登陆用户每分钟访问10次
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.UserRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'user': '10/m'
}
}
# 可以全局使用,局部使用
1.2.4、其他
1.2.4.1、AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户
使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次
1.2.4.2、UserRateThrottle
限制认证用户,使用User id 来区分
使用DEFAULT_THROTTLE_RATES['user'] 来设置频次
1.2.4.3、ScopedRateThrottle
限制用户对于每个视图的访问频次,使用ip或user id
class ContactListView(APIView):
throttle_scope = 'contacts'
...
class ContactDetailView(APIView):
throttle_scope = 'contacts'
...
class UploadView(APIView):
throttle_scope = 'uploads'
...
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.ScopedRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'contacts': '1000/day',
'uploads': '20/day'
}
}
1.2.5、全局配置中设置访问频率
'DEFAULT_THROTTLE_RATES': {
'anon': '3/minute',
'user': '10/minute'
}
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView
from rest_framework.throttling import UserRateThrottle
class StudentAPIView(RetrieveAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
authentication_classes = [SessionAuthentication]
permission_classes = [IsAuthenticated]
throttle_classes = (UserRateThrottle,)
1.2.6、内置频率类使用总结
1、使用
局部使用
throttle_classes = [auth.MyThrottle,]
全局使用
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES':['app01.auth.MyThrottle',],
}
2、内置频率类
BaseThrottle: 基类
AnonRateThrottle: 限制匿名用户的访问次数
SimpleRateThrottle: 咱么自定义扩写它
ScopedRateThrottle
UserRateThrottle: 限制登录用户访问次数
3、扩展内置频率类
定义一个类,继承SimpleRateThrottle
class MySimpleThrottle(SimpleRateThrottle):
scope = 'xxx'
def get_cache_key(self, request, view):
# 以ip限制
return self.get_ident(request)
在setting.py中配置
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES' : {
'xxx':'10/m' # key跟scope对应,value是一个时间
}
}
可以在局部使用和全局使用
4、源码分析
继承SimpleRateThrottle ---> allow_request
5、其它内置频率类
限制未登录用户的频率(AnonRateThrottle)(根据ip限制)
使用:
局部使用,全局使用
setting.py中配置
'DEFAULT_THROTTLE_RATES' : {
'anon':'1/m'
}
限制登录用户访问次数UserRateThrottle(根据用户id限制)
使用:
局部使用,全局使用
setting.py中配置
'DEFAULT_THROTTLE_RATES' : {
'user':'1/m'
}
2、内置过滤功能及第三方过滤功能
2.1、内置过滤功能
过滤: 筛选查询结果
内置筛选的使用
在视图类中配置:
from rest_framework.filters import SearchFilter
filter_backends =[SearchFilter,]
search_fields=('name',) # 表模型中的字段
查询的时候:
http://127.0.0.1:8000/students/?search=e # 支持模糊匹配
2.2、第三方扩展的过滤功能
# 对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持
pip install django-filter # 最新版本(2.4.0)要跟django2.2以上搭配
from django_filters.rest_framework import DjangoFilterBackend
# 在配置文件中增加过滤后端的设置
INSTALLED_APPS = [
...
'django_filters', # 需要注册应用
]
# 全局配置
REST_FRAMEWORK = {
...
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}
# 在视图中添加filter_fields属性,指定可以过滤的字段
class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentSerializer
# 类视图配置
filter_backends = [DjangoFilterBackend, ]
filter_fields = ['id', 'gender', 'name']
查询的时候
# http://127.0.0.1:8000/stu/?gender=1
2.3、排序
对于列表数据,REST framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序
使用方法:
在类视图中设置filter_backends,使用rest_framework.filters.OrderingFilter过滤器,REST framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据集进行排序。
前端可以传递的ordering参数的可选字段值需要在ordering_fields中指明
class StudentListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
filter_backends = [OrderingFilter]
ordering_fields = ('id', 'age')
# 127.0.0.1:8000/books/?ordering=-age
# -id 表示针对id字段进行倒序排序
# id 表示针对id字段进行升序排序
如果需要在过滤以后再次进行排序,则需要两者结合
from rest_framework.generics import ListAPIView
from students.models import Student
from .serializers import StudentModelSerializer
from django_filters.rest_framework import DjangoFilterBackend
class Student3ListView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
filter_fields = ('age', 'gender')
# 因为局部配置会覆盖全局配置,所以需要重新把过滤组件核心类再次声明,
# 否则过滤功能会失效
filter_backends = [OrderingFilter,DjangoFilterBackend]
ordering_fields = ('id', 'age')
3、分页Pagination
REST framework提供了分页的支持
我们可以在配置文件中设置全局的分页方式,如:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 100 # 每页数目
}
可通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_clas属性来指明
class LargeResultsSetPagination(PageNumberPagination):
page_size = 1000
page_size_query_param = 'page_size'
max_page_size = 10000
class BookDetailView(RetrieveAPIView):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
pagination_class = LargeResultsSetPagination
注意:如果在视图内关闭分页功能,只需在视图内设置
pagination_class = None
3.1、可选分页器
3.1.1、PageNumberPagination
前端访问网址形式
GET http://127.0.0.1:8000/students/?page=4
可以在子类中定义的属性:
- page_size 每页数目
- page_query_param 前端发送的页数关键字名,默认为”page”
- page_size_query_param 前端发送的每页数目关键字名,默认为None
- max_page_size 前端最多能设置的每页数量
# APIView
from rest_framework.pagination import PageNumberPagination
# 一 基本使用: url=url=http://127.0.0.1:8000/pager/?page=2&size=3,size无效
class Pager(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据
ret=models.Book.objects.all()
# 创建分页对象
page=PageNumberPagination()
# 在数据库中获取分页的数据
page_list=page.paginate_queryset(ret,request,view=self)
# 对分页进行序列化
ser=BookSerializer1(instance=page_list,many=True)
return Response(ser.data)
# 二 自定制: url=http://127.0.0.1:8000/pager/?page=2&size=3
# size=30,无效,最多5条
class Mypage(PageNumberPagination):
page_size = 2
page_query_param = 'page'
# 定制传参
page_size_query_param = 'size'
# 最大一页的数据
max_page_size = 5
class Pager(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据
ret=models.Book.objects.all()
# 创建分页对象
page=Mypage()
# 在数据库中获取分页的数据
page_list=page.paginate_queryset(ret,request,view=self)
# 对分页进行序列化
ser=BookSerializer1(instance=page_list,many=True)
# return Response(ser.data)
# 这个也是返回Response对象,但是比基本的多了上一页,下一页,和总数据条数(了解即可)
return page.get_paginated_response(ser.data)
#ListAPIView
# 声明分页的配置类
from rest_framework.pagination import PageNumberPagination
class StandardPageNumberPagination(PageNumberPagination):
# 默认每一页显示的数据量
page_size = 2
# 允许客户端通过get参数来控制每一页的数据量
page_size_query_param = "size"
max_page_size = 10
# 自定义页码的参数名
page_query_param = "p"
class StudentAPIView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
pagination_class = StandardPageNumberPagination
# 127.0.0.1/four/students/?p=1&size=5
3.1.2、LimitOffsetPagination
前端访问网址形式
GET http://127.0.0.1/four/students/?limit=100&offset=400
可以在子类中定义的属性:
- default_limit 默认限制,默认值与PAGE_SIZE设置一直
- limit_query_param limit参数名,默认’limit’
- offset_query_param offset参数名,默认’offset’
- max_limit 最大limit限制,默认None
# APIView
# http://127.0.0.1:8000/pager/?offset=4&limit=3
from rest_framework.pagination import LimitOffsetPagination
# 也可以自定制,同简单分页
class Pager(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据
ret=models.Book.objects.all()
# 创建分页对象
page=LimitOffsetPagination()
# 在数据库中获取分页的数据
page_list=page.paginate_queryset(ret,request,view=self)
# 对分页进行序列化
ser=BookSerializer1(instance=page_list,many=True)
# return page.get_paginated_response(ser.data)
return Response(ser.data)
#ListAPIView
from rest_framework.pagination import LimitOffsetPagination
class StandardLimitOffsetPagination(LimitOffsetPagination):
# 默认每一页查询的数据量,类似上面的page_size
default_limit = 2
limit_query_param = "size"
offset_query_param = "start"
class StudentAPIView(ListAPIView):
queryset = Student.objects.all()
serializer_class = StudentModelSerializer
# 调用页码分页类
# pagination_class = StandardPageNumberPagination
# 调用查询偏移分页类
pagination_class = StandardLimitOffsetPagination
3.1.3、CursorPagination
前端访问网址形式
GET http://127.0.0.1/four/students/?cursor=cD0xNQ%3D%3D
可以在子类中定义的属性:
- cursor_query_param:默认查询字段,不需要修改
- page_size:每页数目
- ordering:按什么排序,需要指定
#APIView
from rest_framework.pagination import CursorPagination
# 看源码,是通过sql查询,大于id和小于id
class Pager(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据
ret=models.Book.objects.all()
# 创建分页对象
page=CursorPagination()
page.ordering='nid'
# 在数据库中获取分页的数据
page_list=page.paginate_queryset(ret,request,view=self)
# 对分页进行序列化
ser=BookSerializer1(instance=page_list,many=True)
# 可以避免页码被猜到
return page.get_paginated_response(ser.data)
# ListAPIView
class MyCursorPagination(CursorPagination):
page_size=2
ordering='-id'
from rest_framework.generics import ListAPIView
class AuthorListView(ListAPIView):
serializer_class = serializers.AuthorModelSerializer
queryset = models.Author.objects.filter(is_delete=False)
pagination_class =MyCursorPagination
3.1.4、应用
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
class MyPageNumberPagination(PageNumberPagination):
page_size = 2
page_query_param = 'page'
# 定制传参
page_size_query_param = 'size'
# 最大一页的数据
max_page_size = 5
class MyLimitOffsetPagination(LimitOffsetPagination):
default_limit = 2
# 最大一页的数据
max_limit = 5
class MyCursorPagination(CursorPagination):
page_size=2
ordering='-id'
from rest_framework.generics import ListAPIView
class AuthorListView(ListAPIView):
serializer_class = serializers.AuthorModelSerializer
queryset = models.Author.objects.filter(is_delete=False)
pagination_class =MyCursorPagination
3.2、三种分页的使用总结
1、内置了三种分页器
PageNumberPagination: 普通分页
LimitOffsetPagination: 偏移分页
CursorPagination: 游标分页
2、APIView和GenericAPIView+ListModelMixin
3、GenericAPIView+ListModelMixin的分页模式
4、PageNumberPagination: 普通分页(使用频率最多)
page_size = api_settings.PAGE_SIZE # 每页显示多少条
page_query_param = 'page' # 查询参数
page_size_query_param = size # 查询的时候指定每页显示多少条
max_page_size = 10 # 每页最多显示多少条
使用方式:
定义一个类,继承PageNumberPagination
重写四个属性
在继承了GenericAPIView+ListModelMixin视图类中配置
pagination_class = MyPageNumberPagination
查询
http://127.0.0.1:8000/students/?page=1&size=5
5、LimitOffsetPagination: 偏移分页
default_limit = api_settings.PAGE_SIZE # 默认条数
limit_query_param = 'limit' # 查询时,指定查询多少条
offset_query_param = 'offset' # 查询时,指定的起始位置是哪
max_limit = None # 查询时,最多返回多少条
使用方式:
定义一个类,继承LimitOffsetPagination
重写四个属性
在继承了GenericAPIView+ListModelMixin视图类中配置
pagination_class = MyPageNumberPagination
查询
http://127.0.0.1:8000/students/?limit=100&offset=1
6、CursorPagination: 游标分页(速度块)
cursor_query_param = 'cursor' # 查询的时候,指定的查询方式
page_size = api_settings.PAGE_SIZE # 每页显示多少条
ordering = '-created' # 排序方式
page_size_query_param = size # 查询的时候指定每页显示多少条
max_page_size = None # 每页最多显示多少条
使用方式:
定义一个类,继承CursorPagination
重写四个属性
在继承了GenericAPIView+ListModelMixin视图类中配置
pagination_class = MyPageNumberPagination
查询
http://127.0.0.1:8000/students/?cursor=cD0xNQ%3D%3D
7、APIView的分页模式
新建一个类,继承普通分页,重写四个属性
视图类写法如下:
class StudentApiView(APIView):
def get(self,request):
student_list=Student.objects.all()
page=MyPageNumberPagination() # 实例化得到对象
# 只需要换不同的分页类即可
res=page.paginate_queryset(student_list,request,self) # 开始分页
ser=StudentSerializer(res,many=True)
return page.get_paginated_response(ser.data) # 返回数据