05 drf 频率限制,filter,django-filter
1 模型层choice字段使用(重点)
-
模型表:Student表,写接口应该选择继承哪个视图类
-
推荐使用自动生成路由的方式(继承ViewSetMixin及它的字类)
-
但是目前来说,你先实现功能即可(至于选择哪个,慢慢体会)
-
choice的使用
在模型类中使用 sex = models.SmallIntegerField(choices=((1, '男'), (2, '女'), (3, '未知')), default=1) -在视图类中,在序列化类中 -get_字段名_dispaly()的方法,该方法获得choice字段对应的数据
-
choice的示例
class Student(models.Model):
name = models.CharField(max_length=32)
sex = models.SmallIntegerField(choices=((1, '男'), (2, '女'), (3, '未知')), default=1)
sex = models.SmallIntegerField(choices=choice_sex, default=1)
age = models.IntegerField()
2 自定义频率类(分析,了解)
2.1如何限制频率
如果没有登录就限制ip,登录了限制用户的唯一id
2.2:自定义频率
from rest_framework.throttling import BaseThrottle
class MyThrottle(BaseThrottle):
VISIT_RECORD = {} # 存用户访问信息的大字典
def __init__(self):
self.history = None
def allow_request(self,request,view):
# 根据ip进行频率限制,每分钟只能访问3次
# 限制的逻辑
'''
#(1)取出访问者ip
#(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
#(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
#(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
#(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
'''
# (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])
2.3:示例
urls.py
from django.urls import path
from learn import views
urlpatterns = [
path('books/', views.Book.as_view({'get':'get','post':'post'})),
path('book/<int:pk>/', views.Book.as_view({'get':'retrieve','put':'put','delete':"delete"})),
path('publishs/', views.Publish.as_view({'get':'get','post':'post'})),
path('publish/<int:pk>/', views.Publish.as_view({'get':'retrieve','put':'put','delete':"delete"})),
]
serializer
from . import models
from rest_framework import serializers
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ['id','title','price','publish','publish_name']
extra_kwargs = {
'id': {'required': False},
'publish': {'write_only': True},
'publish_name': {'read_only': True},
}
class PublishModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publish
fields = ['id', 'name', 'addr']
extra_kwargs = {
'id': {'required': False},
}
modles.py
from django.db import models
# Create your models here.
class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=255)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True, db_constraint=False)
@property
def publish_name(self):
return {'id': self.publish_id, 'name': self.publish.name, 'addr': self.publish.addr }
class Publish(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
addr = models.CharField(max_length=255)
utils.py
from rest_framework.throttling import BaseThrottle
class MyThrottle(BaseThrottle):
VISIT_RECORD = {} # 存用户访问信息的大字典
def __init__(self):
self.history = None
def allow_request(self,request,view):
print(view,'这个是视图函数')
# 根据ip进行频率限制,每分钟只能访问3次
# 限制的逻辑
'''
#(1)取出访问者ip
#(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
#(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
#(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
#(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
'''
# (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])
views.py
from . import models
from . import serializer
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
from . import utils
class Book(ViewSet):
throttle_classes = [utils.MyThrottle]
def get(self, request, *args, **kwargs):
book_obj = models.Book.objects.all()
ser = serializer.BookModelSerializer(instance=book_obj, many=True)
return Response(ser.data)
def retrieve(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.Book.objects.get(pk=pk)
ser = serializer.BookModelSerializer(instance=obj)
return Response(ser.data)
def post(self, request, *args, **kwargs):
ser = serializer.BookModelSerializer(data=request.data)
ser.is_valid(raise_exception=True)
ser.save()
return Response(ser.data)
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.Book.objects.get(pk=pk)
ser = serializer.BookModelSerializer(instance=obj, data=request.data)
ser.is_valid(raise_exception=True)
return Response(ser.data)
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
models.Book.objects.filter(pk=pk).delete()
return Response()
class Publish(ViewSet):
throttle_classes = [utils.MyThrottle]
def get(self, request, *args, **kwargs):
book_obj = models.Publish.objects.all()
ser = serializer.PublishModelSerializer(instance=book_obj, many=True)
return Response(ser.data)
def retrieve(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.Publish.objects.get(pk=pk)
ser = serializer.PublishModelSerializer(instance=obj)
return Response(ser.data)
def post(self, request, *args, **kwargs):
ser = serializer.PublishModelSerializer(data=request.data)
ser.is_valid(raise_exception=True)
ser.save()
return Response(ser.data)
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.Publish.objects.get(pk=pk)
ser = serializer.PublishModelSerializer(instance=obj, data=request.data)
ser.is_valid(raise_exception=True)
return Response(ser.data)
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
models.Publish.objects.filter(pk=pk).delete()
return Response()
3 内置频率类,及其使用
3.1:局部使用
class Test(APIView):
def get(self,request,*args,**kwargs):
pass
throttle_classes = [learn.utils.MyThrottle,]
3.2:局部禁用
class Test(APIView):
def get(self,request,*args,**kwargs):
pass
throttle_classes = []
3.3:全局使用
setings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES':['learn.auth.MyThrottle',],
}
3.4:内置频率类
- BaseThrottle:基类
- AnonRateThrottle:限制匿名用户的访问次数
- SimpleRateThrottle:咱么自定义改写它
- ScopedRateThrottle:
- UserRateThrottle:限制登录用户访问次数
3.5:常用内置频率类使用
全局使用
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/day'
}
}
4:如何自定义频率类
4.1:写一个类,继承SimpleRateThrottle
class MySimpleThrottle(SimpleRateThrottle):
scope = 'ip' #全局文件中要用,不写报错
def get_cache_key(self, request, view):
#以ip限制
return self.get_ident(request)
setting.py中配置
REST_FRAMEWORK = {
# 这个是必须要配置,凡是继承了rest-framework的频率类,下面必不可少,如果没有报错
'DEFAULT_THROTTLE_RATES' : {
'ip':'10/m'# key跟scope对应,value是一个时间
}
}
4.2:如何使用自定义频率类
4.2.1:局部使用
class Test(APIView):
def get(self,request,*args,**kwargs):
pass
throttle_classes = [learn.utils.MySimpleThrottle,]
4.2.2:全局使用
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES':['learn.utils.MySimpleThrottle',],
}
4.3:频率类源码分析
流程代码
核心代码
1
2
3:
4
5
4.4其他的内置频率类使用
4.4.1:未登录用户全局使用
全局使用
settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/m',
}
}
局部配置
settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'anon': '100/m',
}
}
views.py
from rest_framework.throttling import AnonRateThrottle
class Test(APIView):
class = [AnonRateThrottle,]
def get(self,request,*args,**kwargs):
pass
4.4.2登录用户
全局使用
settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.UserRateThrottle'
),
'DEFAULT_THROTTLE_RATES': {
'user': '1000/day'
}
}
局部配置
setings
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'user': '1000/day'
}
}
views.py
from rest_framework.throttling import UserRateThrottle
class Test(APIView):
class = [UserRateThrottle,]
def get(self,request,*args,**kwargs):
pass
5扩展其它源码
5.1:AnonRateThrottle源码
5.2:UserRateThrottle源码
5.3:ScopeRateThrottle源码
5.3.1:ScopRateThrottle使用
views.py
from django.shortcuts import render
# Create your views here.
from . import models
from . import serializer
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
class Book(ViewSet):
throttle_scope='book'
def get(self, request, *args, **kwargs):
book_obj = models.Book.objects.all()
ser = serializer.BookModelSerializer(instance=book_obj, many=True)
return Response(ser.data)
def retrieve(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.Book.objects.get(pk=pk)
ser = serializer.BookModelSerializer(instance=obj)
return Response(ser.data)
def post(self, request, *args, **kwargs):
ser = serializer.BookModelSerializer(data=request.data)
ser.is_valid(raise_exception=True)
ser.save()
return Response(ser.data)
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.Book.objects.get(pk=pk)
ser = serializer.BookModelSerializer(instance=obj, data=request.data)
ser.is_valid(raise_exception=True)
return Response(ser.data)
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
models.Book.objects.filter(pk=pk).delete()
return Response()
class Publish(ViewSet):
throttle_scope = 'publish'
def get(self, request, *args, **kwargs):
book_obj = models.Publish.objects.all()
ser = serializer.PublishModelSerializer(instance=book_obj, many=True)
return Response(ser.data)
def retrieve(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.Publish.objects.get(pk=pk)
ser = serializer.PublishModelSerializer(instance=obj)
return Response(ser.data)
def post(self, request, *args, **kwargs):
ser = serializer.PublishModelSerializer(data=request.data)
ser.is_valid(raise_exception=True)
ser.save()
return Response(ser.data)
def put(self, request, *args, **kwargs):
pk = kwargs.get('pk')
obj = models.Publish.objects.get(pk=pk)
ser = serializer.PublishModelSerializer(instance=obj, data=request.data)
ser.is_valid(raise_exception=True)
return Response(ser.data)
def delete(self, request, *args, **kwargs):
pk = kwargs.get('pk')
models.Publish.objects.filter(pk=pk).delete()
return Response()
setings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.ScopedRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'book': '5/m',
'publish': '10/m'
},
}
6 内置,排序,第三方过滤功能
6.1:通过条件筛选结果
筛选是绝对匹配
views.py
from rest_framework.filters import SearchFilter
class Book(ModelViewSet):
throttle_scope = 'book'
serializer_class = serializer.BookModelSerializer
queryset = models.Book.objects.all()
filter_backends =[SearchFilter,]
search_fields=('name','age') # 表模型中的字段
查询
http://127.0.0.1:8000/book/?search=1
6.2:排序功能
views.py
from rest_framework.filters import SearchFilter,OrderingFilter
class Book(ModelViewSet):
throttle_scope = 'book'
serializer_class = serializer.BookModelSerializer
queryset = models.Book.objects.all()
filter_backends = [OrderingFilter, ]
ordering_fields=['id','title'] # 表模型中的字段
查询
http://127.0.0.1:8000/book/?ordering=id
http://127.0.0.1:8000/book/?ordering=-id
http://127.0.0.1:8000/book/?ordering=title
http://127.0.0.1:8000/book/?ordering=-title
6.3:第三方筛选
pip3 install django-filter :最新版本(2.4.0)要跟django2.2以上搭配
views.py
from django_filters.rest_framework import DjangoFilterBackend
class Book(ModelViewSet):
throttle_scope = 'book'
serializer_class = serializer.BookModelSerializer
queryset = models.Book.objects.all()
filter_backends = [DjangoFilterBackend , ]
filter_fields=['id','title'] # 过滤模型字段
查询
127.0.0.1:8000/book/?title=linux基础&id=1
127.0.0.1:8000/book/?title=linux基础
127.0.0.1:8000/book/?id=1
7 排序和筛选一起来
views.py
from rest_framework.filters import OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
class Book(ModelViewSet):
throttle_scope = 'book'
serializer_class = serializer.BookModelSerializer
queryset = models.Book.objects.all()
filter_backends = [OrderingFilter, DjangoFilterBackend]
ordering_fields = ('id', 'price')
filter_fields = ['title', 'id']
查询
http://127.0.0.1:8000/book/?title=linux基础&id=1&ordering=-age,-id
http://127.0.0.1:8000/book/?title=linux基础&id=1
http://127.0.0.1:8000/book/?title=linux基础&id=1&ordering=-id
http://127.0.0.1:8000/book/?title=linux基础&id=1&ordering=-age
8:扩展示例
8.1:编写登录接口,匿名用户一分钟访问3次,登录用户一分钟访问10次
views.py
from django.shortcuts import render
# Create your views here.
import uuid
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework import views
from rest_framework import mixins
from . import models
from . import serializer
from . import utils
class Book(viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.CreateModelMixin):
authentication_classes = [utils.LoginBaseAuthentication,]
throttle_classes = [utils.LoginThrottle,utils.UnLoginThrottle]
serializer_class = serializer.BookModelSerializer
queryset = models.Book.objects.all()
class Login(views.APIView):
def post(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
try:
user_obj = models.UserInfo.objects.get(username=username, password=password)
token = uuid.uuid4()
user_obj.token = token
user_obj.save()
return Response({'code': 200, 'msg': '登录成功', 'token': token})
except Exception:
return Response({'code': 100, 'msg': '用户名或者密码错误'})
urls.py
from django.urls import path
from work import views
urlpatterns = [
path('login/', views.Login.as_view()),
path('book/', views.Book.as_view({'get':'list','post':'create'})),
]
modles.py
from django.db import models
# Create your models here.
class Book(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=255)
price = models.CharField(max_length=255)
publish = models.ForeignKey(to='Publish',on_delete=models.SET_NULL,db_constraint=False,null=True)
class Publish(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
addr = models.CharField(max_length=255)
class UserInfo(models.Model):
username = models.CharField(max_length=255,unique=True)
password = models.CharField(max_length=255)
token = models.CharField(max_length=255,null=True)
user_type = models.IntegerField(choices=((1,'vip'),(2,'generic')),default=1)
setings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'unlogin': '3/m',
'login':'10/m'
}
}
utils.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.throttling import SimpleRateThrottle
from rest_framework.exceptions import AuthenticationFailed, Throttled
from . import models
class LoginBaseAuthentication(BaseAuthentication):
def authenticate(self, request):
#
# token = request.data.get('token') or request.GET.get('token')
#
# if not token:
# # raise AuthenticationFailed('token关键字')
# pass
# try:
# user = models.UserInfo.objects.get(token=token)
# return user,token
# except Exception:
# raise AuthenticationFailed('用户名或则密码错误!')
token = request.data.get('token') or request.GET.get('token')
if token:
try:
user = models.UserInfo.objects.get(token=token)
return user, token
except Exception as e:
print(e)
pass
class LoginThrottle(SimpleRateThrottle):
scope = 'login'
def get_cache_key(self, request, view):
request.id = 0 # 设置了一个标志位
id = request.user.pk
if id: # 如果有值表示登录了
request.id = id
return id
return None
class UnLoginThrottle(SimpleRateThrottle):
scope = 'unlogin'
def get_cache_key(self, request, view):
if request.id: # 如果用户登录了request.id为True
return None
return self.get_ident(request)
serializer
from rest_framework import serializers
from . import models
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ['id', 'title', 'price', 'publish', 'publish_id']
extra_kwargs = {'id': {'required': False},
}
publish = serializers.SerializerMethodField(read_only=True)
publish_id = serializers.CharField(write_only=True)
def get_publish(self, obj):
return {'id': obj.publish.id, 'name': obj.publish.name, 'addr': obj.publish.addr}
class PublishModelSerializer(serializers.ModelSerializer):
class Meta:
mode = models.Publish
fields = ['id', 'name', 'addr']
extra_kwargs = {
'id': {'required': False}
}
8.2 通过内置过滤器实现
-http://127.0.0.1:8000/students/?search=金
-查询出书名中带金的所有图书
import uuid
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework import views
from rest_framework import mixins
from . import models
from . import serializer
from . import utils
from rest_framework.filters import SearchFilter
class Book(viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.CreateModelMixin):
authentication_classes = [utils.LoginBaseAuthentication,]
throttle_classes = [utils.LoginThrottle,utils.UnLoginThrottle]
serializer_class = serializer.BookModelSerializer
queryset = models.Book.objects.all()
filter_backends = (SearchFilter,)
search_fields = ('id','title','price')
8.3 通过内置排序实现
http://127.0.0.1:8000/book/?ordering=price
-按价格升序,降序排列
from rest_framework.filters import SearchFilter,OrderingFilter
class Book(viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.CreateModelMixin):
authentication_classes = [utils.LoginBaseAuthentication,]
throttle_classes = [utils.LoginThrottle,utils.UnLoginThrottle]
serializer_class = serializer.BookModelSerializer
queryset = models.Book.objects.all()
filter_backends = (OrderingFilter,)
ordering_fields = ['price']
8.4 通过django-filter过滤器实现
http://127.0.0.1:8000/book/?title=金瓶没&price=12
-查询出符合条件的图书
from django_filters.rest_framework import DjangoFilterBackend
class Book(viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.CreateModelMixin):
authentication_classes = [utils.LoginBaseAuthentication,]
throttle_classes = [utils.LoginThrottle,utils.UnLoginThrottle]
serializer_class = serializer.BookModelSerializer
queryset = models.Book.objects.all()
filter_backends = [DjangoFilterBackend, ]
filter_fields=['title','price']
8.5:通过django-filter和内置排序实现
http://127.0.0.1:8000/students/?title=金瓶没&odering=price
from rest_framework.filters import SearchFilter,OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
class Book(viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.CreateModelMixin):
authentication_classes = [utils.LoginBaseAuthentication,]
throttle_classes = [utils.LoginThrottle,utils.UnLoginThrottle]
serializer_class = serializer.BookModelSerializer
queryset = models.Book.objects.all()
filter_backends = [OrderingFilter, DjangoFilterBackend]
ordering_fields = ('id', 'price')
filter_fields = ['title', 'id','price']