ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Day84--drf05--频率、过滤、排序与分页

2021-12-09 02:31:37  阅读:110  来源: 互联网

标签:name -- price token 过滤 user Day84 drf05 class


内容回顾

# drf:方便我们在django框架上写出符合restful规范的接口
# 请求和响应
    -请求类的对象
    -请求解析编码格式:局部,全局
    
    -响应对象
    	-data,status,header
    -响应格式:浏览器,json
         
# 序列化类
    -Serializer
    -ModelSerializer(用的多,表模型有对应关系)

# 视图类
    -两个视图基类
        -APIView:执行流程
        -GenericAPIView:两个类属性,三个方法
    -5个视图扩展类(不是视图类)
    	-需要配合GenericAPIView使用
    -9个视图子类
    -视图集
    	-ModelViewSet
        -ReadOnlyModelViewSet
        -ViewSet
        -GenericViewSet
        -ViewSetMixin
        
 # 路由
    -自动生成路由(SimpleRouter,DefaultRouter)
    -action装饰器
    
 # 认证类
    -写一个类继承BaseAuthentication重写authenticate在里面判断用户是否登录,如果登录了返回两个值,第一个值必须是当前登录用户,如果认证失败,抛出异常
    -局部使用
    -全局使用
    
# 权限类
    -写一个类继承BasePermission重写has_permission在里面判断用户是否有权限,如果有,return True,否则return False
    -局部使用
    -全局使用

今日内容

1. drf之频率限制

# 限制用户的访问次数:根据用户ip地址限制

例:一个IP地址,一分钟之内只允许访问三次

# 获取访问者IP地址
    -request.META.get('REMOTE_ADDR')  # REMOTE:远程
    
# request.META:请求头中所有的数据
# 自定义请求头的数据,也在request.META中,且给你转成 key为 'HTTP_大写'的形式

1.1 频率类定义

# 1.写一个类,继承SimpleRateThrottle,重写get_cache_key(),返回什么就以什么做限制 (返回ip就以ip限制)
# 2.在类中写一个类属性:scope = 'ip_m_3'  值自定义,但要在配置文件中配置一个与之对应的值

# 写一个频率限制类,根据ip地址 一分钟只能访问三次
from rest_framework.throttling import SimpleRateThrottle
class MyThrotting(SimpleRateThrottle):
    scope = 'ip_m_3'

    def get_cache_key(self, request, view):
        # return request.META.get('REMOTE_ADDR')
        return self.get_ident(request)  # 内置的get_ident()就是返回访问者的ip,是父类BaseThrottle的方法


# 3.在settings.py中写
REST_FRAMEWORK = {
    'DEFAULT_THROTTLE_RATES': {
        'ip_m_3': '3/m',  # key值是频率类中scop字段对应的值,value是访问次数限制
    }
}

1.2 频率类使用

# 局部使用
-配置在视图类中
    class IndexView(APIView):
        throttle_classes = [MyThrotting, ]
            
# 全局使用            
-配置在settings.py中
    REST_FRAMEWORK = {
    	'DEFAULT_THROTTLE_CLASSES': ['app01.auth.MyThrotting',],
    }

1.3 内置的频率类

from rest_framework.throttling import AnonRateThrottle, UserRateThrottle, ScopeRateThrottle

# AnonRateThrottle
限制所有匿名未认证用户,使用IP区分用户
setting中 'DEFAULT_THROTTLE_RATES': {'anon': '5/m',}  设置频次

# UserRateThrottle
限制认证用户,使用User id 来区分  # 但是认证用户,也必须使用内置的认证类:SessionAuthentication
setting中 'DEFAULT_THROTTLE_RATES': {'user': '10/m',}  设置频次

# ScopeRateThrottle
限制用户对于每个视图的访问频次,使用IP或User id


# 例1:限制未登录用户1分钟访问5次
from rest_framework.throttling import AnonRateThrottle
class TestView1(APIView):
    # 配置内置频率类 (局部配置)
    throttle_classes = [AnonRateThrottle]
    def get(self,request,*args,**kwargs):
        return Response('我是未登录用户')
    
-配置在settings.py中
    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_RATES': {'anon': '5/m',}
    }
    
    
# 例2:限制登录用户1分钟访问10次    
from rest_framework.authentication import SessionAuthentication
from rest_framework.throttling import UserRateThrottle
class TestView2(APIView):
    # 配置内置认证类 (局部配置)
    authentication_classes=[SessionAuthentication]
    # 配置内置频率类 (局部配置)
    throttle_classes = [UserRateThrottle]
    def get(self,request,*args,**kwargs):
        return Response('我是登录用户')
    
-配置在settings.py中
    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_RATES': {'user': '10/m',}
    } 
    
    
# 或直接例1和例2 采用全局配置
全局:在setting中
  'DEFAULT_THROTTLE_CLASSES': (
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ),
    'DEFAULT_THROTTLE_RATES': {
        'user': '10/m',
        'anon': '5/m',
    }   

2. drf之过滤与排序

# 请求地址中带过滤条件 (条件过滤、排序过滤)

# 配置使用:
# 1.局部配置
在视图函数中配置: filter_backends= [内置,第三方,自己写]
# 2.全局配置
在setting中: 'DEFAULT_FILTER_BACKENDS': (内置,第三方,自己写)

# 注意:
1.获取所有的数据 才需要过滤条件
2.必须继承GenericAPIView及其子类,才能使用filter_backends 
(因为除了三大认证:认证,权限,频率,剩下的过滤、排序、分页都需要queryset)

3.因内置的过滤和排序类,使用时很固定,必须是search=值 、ordering=值 
故:通常过滤类 不使用内置的,使用第三方(django-filter)或者自定义过滤类,排序类使用内置的就可以了

# 自己读源码发现:第三条的原因是错误的,是可以修改查询条件的参数名,且有一定的查询条件(但还是太少,且不方便)
思路:继承内置的SearchFilter类,重写父类的 search_param参数,就可以按照自己给的参数进行查询

from rest_framework.filters import SearchFilter
class FilterByName(SearchFilter):
    search_param = 'name'

2.1 内置过滤类使用

from .models import Book
from .serializer import BookSerializer
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin

# 过滤功能的使用
from rest_framework.filters import SearchFilter,OrderingFilter  # 条件过滤、排序过滤
class BookView(GenericViewSet, ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    # 配置过滤类
    filter_backends = [SearchFilter,]
    # 配置要过滤的字段
    search_fields=['name',]  # books/?search=红   name中 带红都会查出来
    search_fields=['name','price'] # books/?search=红  name中或者price中 带红都会查出来
    
    # 特殊字符+配置过滤的字段 ===> 起到额外的筛选作用   自己读源码获知(了解即可)
    # 默认是'icontains'  忽略大小写的包含
    lookup_prefixes = {     # 查询特殊符 做前缀
        '^': 'istartswith', # 以什么开始
        '=': 'iexact',      # 完整的查
        '@': 'search',      # 搜索 (貌似使用有点问题)
        '$': 'iregex',      # 以正则查
    }
    # 例:
    search_fields=['^name',]  # 查找name字段中 以搜索值为开头的
    
# 使用:
http://127.0.0.1:8000/books/?search=红   

2.2 内置排序类使用

from rest_framework.filters import SearchFilter, OrderingFilter
class BookView(GenericViewSet, ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    # 配置排序类
    filter_backends = [OrderingFilter, ]
    # 配置要排序的字段
    # ordering_fields = ['price']   # books/?ordering=-price  按price降序排列
    ordering_fields = ['price','id']   # books/?ordering=-price,-id 按price降序排列,如果price一样,再按id的降序排列
    
# 使用:
http://127.0.0.1:8000/books/?ordering=-price  

2.3 排序和过滤同时使用

# 既有排序,又有过滤
from rest_framework.filters import SearchFilter, OrderingFilter
class BookView(GenericViewSet, ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    # 配置排序类
    filter_backends = [SearchFilter,OrderingFilter, ]  # 先过滤,再排序 (减少排序的数据量)
    # 配置要过滤的字段
    search_fields=['name',]
    # 配置要排序的字段
    ordering_fields = ['price']  # books/?search=记&ordering=-price  查询名字中带记的并且按价格降序排列

2.4 第三方过滤类使用

# 实现:http://127.0.0.1:8000/books/?name=红楼梦&price=12  过滤查询

# django-filter--基本使用(更高级,后面项目讲)
# pip install django-filter

# 第一步:安装 django-filter 模块
# 第二步:setting中注册: INSTALLED_APPS=['django_filters']
# 第三步:使用第三方的过滤类
from django_filters.rest_framework import DjangoFilterBackend
class BookView(GenericViewSet, ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    # 配置第三方过滤类
    filter_backends = [DjangoFilterBackend]
    # 配置要过滤的字段
    filterset_fields=['name','price']  # 注意:和内置的配置过滤字段不同
    
# 使用    
http://127.0.0.1:8000/books/?name=红楼梦&price=12  名字是红楼梦并且价格为12的

2.5 自定义过滤类及使用

# 1.写一个类,继承BaseFilterBackend,重写filter_queryset(),返回过滤完的数据 queryset

# 自定义过滤类:
http://127.0.0.1:8000/books/?price_gt=12  (价格大于12)

from rest_framework.filters import BaseFilterBackend
class MyFilterByPrice(BaseFilterBackend):  
    def filter_queryset(self, request, queryset, view):
        # queryset就是要过滤的数据
        price = request.query_params.get('price_gt')  # 获取条件值
        if price:
            queryset = queryset.filter(price__gt=price)
        return queryset  # 返回过滤完的数据

    
# 自定义过滤类的使用
from .auth import MyFilterByPrice
class BookView(GenericViewSet, ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
    # 配置自定义过滤类
    filter_backends = [MyFilterByPrice]
    # 不用配置要过滤的字段,因为自己写在自定义过滤类中了

3. drf之分页

分页功能 
# 内置了三种分页类:不能直接使用,需要自定义继承,再修改一下几个类属性

3.1 自定义分页类

# 自定义分页类
from rest_framework.pagination import  PageNumberPagination,LimitOffsetPagination,CursorPagination

# 基本分页 (页数分页) --常用
class MyPageNumberPagination(PageNumberPagination):
    # 重写4个类属性即可
    page_size = 2  # 每页默认显示两条
    
    page_query_param = 'page'  # 设置查询条件--页数的参数名,其值对应的是第几页
    # http://127.0.0.1:8000/books/?page=2
    
    page_size_query_param = 'size'  # 设置查询条件--条数的参数名,其值对应的是显示多少条(最大不超过max_page_size)
    # http://127.0.0.1:8000/books/?page=2&size=4 获取第二页数据,返回4条数据
    
    max_page_size = 5  # 设置每页最大显示多少条 
    # http://127.0.0.1:8000/books/?page=2&size=400 获取第二页数据,最多返回5条


# 偏移分页
class MyLimitOffsetPagination(LimitOffsetPagination):
    # 4个类属性
    default_limit = 2   # 每页默认显示多少条
    
    limit_query_param = 'limit'  # 设置偏移条数的参数名,其值对应的是向后偏移多少条 (最大不超过max_limit)
    # http://127.0.0.1:8000/books/?limit=3
    
    offset_query_param = 'offset'  # 设置偏移开始位置的参数名,其值对应的是从第几条开始偏移
    # http://127.0.0.1:8000/books/?limit=3&offset=2  # 从第2条数据位置向后取3条数据
    
    max_limit = 5   # 限制limit最大条数

    
# 游标分页:针对大数据量,只能上一页或下一页,不能跳到具体某一页
# 优势是速度快,但是不能直接跳到某一页
class MyCursorPagination(CursorPagination):
    page_size = 2  # 每页显示多少条
    cursor_query_param = 'cursor'  # 查询条件,无用 (因为没办法确认其具体的值) 
    ordering = 'id' # 按谁排序

3.2 分页类使用

from app01.auth import MyPageNumberPagination as PageNumberPagination
class BookView(GenericViewSet, ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
    # 局部配置分页类
    pagination_class = PageNumberPagination

补充

1.BaseThrottle的get_ident()方法中

xff = requeset.META.get('HTTP_X_FORWARDED_FOR') 
# 'X_FORWARDED_FOR' 是http的请求头,它的作用是拿出所有ip,包括代理

2. 变量后直接加逗号

a=(3,)
a=3,  
print(type(a))  # a是元组

作业

# 1 写一个频率类,限制books接口一分钟只能访问5次 (带基本分页功能)
# 自定义频率类:一分钟只能访问5次
class AccessThrottle(SimpleRateThrottle):
    scope = '5_m'

    def get_cache_key(self, request, view):
        return self.get_ident(request)

# 自定义分页类:基本分页(页数分页)
class PagePagination(PageNumberPagination):
    page_size = 2 
    page_query_param = 'page' 
    page_size_query_param = 'size' 
    max_page_size = 3

2 写Books接口,实现按图书名字查询和按价格排序 (带基本分页功能)

# 重写内置SearchFilter过滤类的 search_param参数,就可以按照自己给的参数名进行查询
class FilterByName(SearchFilter):
    search_param = 'name'
    
# 接口中局部配置:
    # 配置过滤和排序类
    filter_backends = [FilterByName, OrderingFilter]
    # 配置过滤类的字段
    search_fields = ['title']
    # 配置排序类的字段
    ordering_fields = ['price']


3 写一个频率类,限制ip一分钟只能访问三次(继承BaseThrottle),重写allow_request方法,如果允许访问就返回True,如果不允许就返回False

4 自定义实现频率类功能的逻辑
#(1)取出访问者ip
#(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
#(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
#(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
#(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
    
    
  
5 写一个功能,限制年龄小于18岁的用户,每天只能8:00--9:00登录
"""
思路1: 写在自定义认证类中
    这种是都可以登录,但登录之后,校验失败,还是当做没有登录处理
1.根据获取的token,去user_token查找对应的用户
2.再判断用户的年龄字段,和登录时间(在UserToken表中,每次登录会刷新token,也会刷新登录时间)
3.成功就返回用户和token
4.失败,则抛出响应的异常
"""
class LoginAuth(BaseAuthentication):
    # 限制年龄小于18岁的用户,每天只能8:00--9:00登录
    def authenticate(self, request):
        token = request.query_params.get('token')
        user_token_obj = UserToken.objects.filter(token=token).first()
        if not user_token_obj:
            raise AuthenticationFailed('token未传入或不合法')
        if user_token_obj.user.age < 18 and user_token_obj.login_time.hour in (8, 9):
            raise AuthenticationFailed('小于18岁的用户,只能在每天8-9点登录')
        return user_token_obj.user, token
"""
思路2:直接写在登录接口中 (这种是登录接口,再次验证判断)
1.密码验证成功之后
2.再判断用户的年龄字段,和当前登录时间(time.time().year)
3.成功就返回token
4.失败,则返回'小于18岁的用户,只能在每天8-9点登录'
"""
    
    
6 三次密码失败之后,账户被禁用 (管理员用户解锁)
# 拓展:若是连续输错三次,那么就加一个登录时间字段
"""
思路:
登录接口中:
1.先验证用户名
2.判断该用户名的 登录失败次数字段是否大于等于三次,是则返回该用户被锁,不是则验证密码
3.验证成功,登录成功 返回token;
4.验证失败,给该用户表 登录失败次数字段加1 login_fail_number += 1
"""
class LoginView(ViewSet):

    @action(methods=['POST'], detail=False)
    def login(self, request):
        name = request.data.get('name')
		# 1.先验证用户名 是否存在
        name_user = models.LoginUser.objects.filter(name=name).first()
        if not name_user:
            return Response({'code': 101, 'msg': '用户名不存在'})
		# 2.判断该用户名的 登录失败次数字段是否大于等于三次,是则返回该用户被锁,不是则验证密码
        if name_user.login_fail_number >= 3:
            return Response({'code': 101, 'msg': '该用户密码输错已超过三次,被锁定'})

        password = request.data.get('password')
        user = models.LoginUser.objects.filter(name=name, password=password).first()
        # 3. 验证密码,成功就返回token
        if user:
            token = str(uuid4())
            models.UserToken.objects.update_or_create(defaults={'token': token}, user=user)
            return Response({'code': 100, 'msg': '登录成功', 'token': token})
        else:
            # 4. 验证密码,失败就给该用户名的登录失败次数字段加1
            name_user.login_fail_number += 1
            name_user.save()
            return Response({'code': 101, 'msg': '密码错误'})

标签:name,--,price,token,过滤,user,Day84,drf05,class
来源: https://www.cnblogs.com/Edmondhui/p/15665340.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有