zoukankan      html  css  js  c++  java
  • drf-权限、频率(限流)、过滤、排序、异常处理

    1 权限Permissions(权限是在认证之后的)

    权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。

    • 在执行视图的dispatch()方法前,会先进行视图访问权限的判断
    • 在通过get_object()获取具体对象时,会进行模型对象访问权限的判断

    1.1 权限源码分析

    # APIView---->dispatch---->initial--->self.check_permissions(request)(APIView的对象方法)
        def check_permissions(self, request):
            # 遍历权限对象列表得到一个个权限对象(权限器),进行权限认证
            for permission in self.get_permissions():
                # 权限类一定有一个has_permission权限方法,用来做权限认证的
                # 参数:权限对象self、请求对象request、视图类对象
                # 返回值:有权限返回True,无权限返回False
                if not permission.has_permission(request, self):
                    self.permission_denied(
                        request, message=getattr(permission, 'message', None)
                    )

    1.2 自定义权限(和认证一样)

     app_auth.py写一个类,继承BasePermission,重写has_permission

    # 写一个类,继承BasePermission,重写has_permission,如果权限通过,就返回True,不通过就返回False
    from rest_framework.permissions import BasePermission
    
    
    class UserPermission(BasePermission):
        def has_permission(self, request, view):    # view是视图类的对象
            # 不是超级用户,不能访问
            # 由于认证已经过了,request内就有user对象了,当前登录用户
            user = request.user  # 当前登录用户
            # 如果该字段用了choice,通过get_字段名_display()就能取出choice后面的中文
            print(user.get_user_type_display())
            if user.user_type == 1:
                return True
            else:
                return False

    使用:

    # 局部使用
    class TestView(APIView):
        permission_classes = [app_auth.UserPermission]
    # 全局使用
    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",],
        'DEFAULT_PERMISSION_CLASSES': [
            'app01.app_auth.UserPermission',
        ],
    }
    # 局部禁用
    class TestView(APIView):
        permission_classes = []

    1.3 内置权限以及认证

    # 演示一下内置权限的使用:IsAdminUser,控制是否对网站后台有权限的人
    # 1 创建超级管理员
    # 2 写一个测试视图类
    # views.py
    from rest_framework.permissions import IsAdminUser
    from rest_framework.authentication import SessionAuthentication
    class TestView3(APIView):
        authentication_classes=[SessionAuthentication,]
        permission_classes = [IsAdminUser]
        def get(self,request,*args,**kwargs):
            return Response('这是22222222测试数据,超级管理员可以看')
    
    # urls.py
    urlpatterns = [
        path('test3/', views.TestView3.as_view()),
    ]
    
    # 3 超级用户登录到admin,再访问test3就有权限
    # 4 正常的话,普通管理员,没有权限看(判断的是is_staff字段)

    2 频率限制/限流(Throttling)

    可以对接口访问的频次进行限制,以减轻服务器压力。

    一般用于付费购买次数,投票等场景使用.

    2.1 自定义频率类

    # 自定制频率类,需要写两个方法
        -# 判断是否限次:没有限次可以请求True,限次了不可以请求False
            def allow_request(self, request, view):
        -# 限次后调用,显示还需等待多长时间才能再访问,返回等待的时间seconds
            def wait(self):

    自定义频率类

    throttling.py

    # 自定制的频率限制类
    import time
    class IPThrottle():
        #定义成类属性,所有对象用的都是这个
        VISIT_DIC = {}
        def __init__(self):
            self.history_list=[]
        def allow_request(self, request, view):
            '''
            #(1)取出访问者ip
            #(2)判断当前ip不在访问字典里,添加进去,并且直接返回True,表示第一次访问,在字典里,继续往下走
            #(3)循环判断当前ip的列表,有值,并且当前时间减去列表的最后一个时间大于60s,把这种数据pop掉,这样列表中只有60s以内的访问时间,
            #(4)判断,当列表小于3,说明一分钟以内访问不足三次,把当前时间插入到列表第一个位置,返回True,顺利通过
            #(5)当大于等于3,说明一分钟内访问超过三次,返回False验证失败
            '''
    
            ip=request.META.get('REMOTE_ADDR')
            ctime=time.time()
            if ip not in self.VISIT_DIC:
                self.VISIT_DIC[ip]=[ctime,]
                return True
            self.history_list=self.VISIT_DIC[ip]   #当前访问者时间列表拿出来
            while True:
                if ctime-self.history_list[-1]>60:
                    self.history_list.pop() # 把最后一个移除
                else:
                    break
            if len(self.history_list)<3:
                self.history_list.insert(0,ctime)
                return True
            else:
                return False
    
        def wait(self):
            # 当前时间,减去列表中最后一个时间
            ctime=time.time()
    
            return 60-(ctime-self.history_list[-1])

    settings.py

    REST_FRAMEWORK = {
        'PAGE_SIZE': 1,
        'DEFAULT_THROTTLE_CLASSES': (
            'utils.throttling.IPThrottle',
        ),
    
    }

    简单使用:(继承SimpleRateThrottle,重写get_cache_key)

    throttling.py

    # 写一个类,继承SimpleRateThrottle,只需要重写get_cache_key
    from rest_framework.throttling import ScopedRateThrottle,SimpleRateThrottle
    
    #继承SimpleRateThrottle
    class MyThrottle(SimpleRateThrottle):
        scope='luffy'
        def get_cache_key(self, request, view):
            print(request.META.get('REMOTE_ADDR'))
            return request.META.get('REMOTE_ADDR')   # 返回

    views.py

    from rest_framework.response import Response
    from api import models
    from rest_framework.views import APIView
    from api.ser import BookModelSerializer
    from rest_framework.pagination import PageNumberPagination
    
    class MyPageNumberPagination(PageNumberPagination):
        page_size = 1
        page_query_param = "aaa"
    
    # 如果使用APIView分页
    from utils.throttling import MyThrottle
    class BookView(APIView):
        def get(self,request,*args,**kwargs):
            throttle_classes = [MyThrottle, ]
            book_list=models.Book.objects.all()
            # 实例化得到一个分页器对象
            page_cursor=MyPageNumberPagination()
    
            book_list=page_cursor.paginate_queryset(book_list,request,view=self)
            next_url =page_cursor.get_next_link()
            pr_url=page_cursor.get_previous_link()
            # print(next_url)
            # print(pr_url)
            book_ser=BookModelSerializer(book_list,many=True)
            return Response(data=book_ser.data)

    settings.py

    REST_FRAMEWORK = {
        'PAGE_SIZE': 1,
        'DEFAULT_THROTTLE_CLASSES': (
            'utils.throttling.MyThrottle',
        ),
        'DEFAULT_THROTTLE_RATES': {
            'luffy': '3/m'  # key要跟类中的scop对应
        },
    }

    补充:

    # python3 manage.py runserver 0.0.0.0:8000   #局域网就可以相互访问

    2.2 内置频率类

    settings.py设置限制匿名用户访问频次

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",],
        'DEFAULT_PERMISSION_CLASSES': [
            'app01.app_auth.UserPermission',
        ],
        'DEFAULT_THROTTLE_CLASSES': (
            'rest_framework.throttling.AnonRateThrottle',
        ),
        'DEFAULT_THROTTLE_RATES': {
            'anon': '3/m',
        }
    }

    views.py

    class TestView5(APIView):
        authentication_classes = []
        permission_classes = []
        throttle_classes = [AnonRateThrottle]
    
        def get(self, request, *args, **kwargs):
            return Response('我是未登录用户,TestView5')

    内置频率限制之全局或局部限制登录用户的访问频次

    # 需求:未登录用户1分钟访问5次,登录用户一分钟访问10次
    全局:在setting中
      'DEFAULT_THROTTLE_CLASSES': (
            'rest_framework.throttling.AnonRateThrottle',
            'rest_framework.throttling.UserRateThrottle'
        ),
        'DEFAULT_THROTTLE_RATES': {
            'user': '10/m',
            'anon': '5/m',
        }
            
    局部配置:(settings.py中的设置频率的DEFAULT_THROTTLE_RATES还要写)
        然后再在视图类中配一个就行
        throttle_classes = [AnonRateThrottle]

    限制的用户

    1) AnonRateThrottle
    
    限制所有匿名未认证用户,使用IP区分用户。
    
    使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次
    
    2)UserRateThrottle
    
    限制认证用户,使用User id 来区分。
    
    使用DEFAULT_THROTTLE_RATES['user'] 来设置频次
    
    3)ScopedRateThrottle
    
    限制用户对于每个视图的访问频次,使用ip或user id。

    SimpleRateThrottle源码分析

    # SimpleRateThrottle源码分析
        def get_rate(self):
            """
            Determine the string representation of the allowed request rate.
            """
            if not getattr(self, 'scope', None):
                msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
                       self.__class__.__name__)
                raise ImproperlyConfigured(msg)
    
            try:
                return self.THROTTLE_RATES[self.scope]  # scope:'user' => '3/min'
            except KeyError:
                msg = "No default throttle rate set for '%s' scope" % self.scope
                raise ImproperlyConfigured(msg)
        def parse_rate(self, rate):
            """
            Given the request rate string, return a two tuple of:
            <allowed number of requests>, <period of time in seconds>
            """
            if rate is None:
                return (None, None)
            #3  mmmmm
            num, period = rate.split('/')  # rate:'3/min'
            num_requests = int(num)
            duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
            return (num_requests, duration)
        def allow_request(self, request, view):
            if self.rate is None:
                return True
            #当前登录用户的ip地址
            self.key = self.get_cache_key(request, view)  # key:'throttle_user_1'
            if self.key is None:
                return True
    
            # 初次访问缓存为空,self.history为[],是存放时间的列表
            self.history = self.cache.get(self.key, [])
            # 获取一下当前时间,存放到 self.now
            self.now = self.timer()
    
            # Drop any requests from the history which have now passed the
            # throttle duration
    
            # 当前访问与第一次访问时间间隔如果大于60s,第一次记录清除,不再算作一次计数
            # 10 20 30 40
            # self.history:[10:23,10:55]
            # now:10:56
            while self.history and  self.now - self.history[-1] >= self.duration:
                self.history.pop()
    
            # history的长度与限制次数3进行比较
            # history 长度第一次访问0,第二次访问1,第三次访问2,第四次访问3失败
            if len(self.history) >= self.num_requests:
                # 直接返回False,代表频率限制了
                return self.throttle_failure()
    
            # history的长度未达到限制次数3,代表可以访问
            # 将当前时间插入到history列表的开头,将history列表作为数据存到缓存中,key是throttle_user_1,过期时间60s
            return self.throttle_success()

    3 过滤Filtering

    对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。

    # pip3 install django-filter

    在配置文件中注册,并设置全局配,或者局部配置

    INSTALLED_APPS = [
        ...
        'django_filters',  # 需要注册应用,
    ]
    
    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",],
        'DEFAULT_PERMISSION_CLASSES': [
            'app01.app_auth.UserPermission',
        ],
        'DEFAULT_THROTTLE_CLASSES': (
            'rest_framework.throttling.AnonRateThrottle',
            'rest_framework.throttling.UserRateThrottle',
        ),
        'DEFAULT_THROTTLE_RATES': {
            'anon': '5/m',
            'user': '10/m',
        },
        'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)   # 全局过滤,之后在视图中指明按照哪个字段过滤
    }

    views.py

    class BookView(ListAPIView):
        authentication_classes = []
        permission_classes = []
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        filter_fields = ('name',)  # 配置可以按照哪个字段来过滤

    urls.py

    urlpatterns = [
        path('books/', views.BookView.as_view()),
    ]

    访问效果

     过滤的局部配置:

    from django_filters.rest_framework import DjangoFilterBackend  # 导入
    # 之后在视图中使用
    filter_backends = [DjangoFilterBackend]

     4 排序

    对于列表数据,REST framework提供了OrderingFilter过滤器(基于django-filter的)来帮助我们快速指明数据按照指定字段进行排序。

    使用方法:

    在类视图中设置filter_backends,使用rest_framework.filters.OrderingFilter过滤器,REST framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据集进行排序。

    前端可以传递的ordering参数,例如:http://127.0.0.1:8000/books2/?ordering=-id(可选字段值需要在ordering_fields中指明)。

    局部配置

    views.py (局部配置直接在视图中配置)

    from rest_framework.generics import ListAPIView
    from app01.models import Book
    from app01.serializers import BookSerializer
    from django_filters.rest_framework import DjangoFilterBackend
    from rest_framework.filters import OrderingFilter    # 排序,依赖于django-filters
    
    # 过滤字段使用
    class BookView(ListAPIView):
        authentication_classes = []
        permission_classes = []
        filter_backends = [DjangoFilterBackend]
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        filter_fields = ('name', 'price')  # 配置可以按照哪个字段来过滤
    
    # 排序组件使用
    class BookView2(ListAPIView):
        authentication_classes = []
        permission_classes = []
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        filter_backends = [OrderingFilter]   # 局部配置
        ordering_fields = ('id', 'price')    # 排序字段

    urls.py

    from django.contrib import admin
    from django.urls import path
    from app01 import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        # 过滤
        path('books/', views.BookView.as_view()),
        # 排序
        path('books2/', views.BookView2.as_view()),
    ]

    全局配置(settings.py中配置排序,视图中配置排序字段)

    settings.py

    REST_FRAMEWORK = {
        'DEFAULT_FILTER_BACKENDS': (
        'rest_framework.filters.OrderingFilter', )
    }

    views.py

    from rest_framework.generics import ListAPIView
    from app01.models import Book
    from app01.serializers import BookSerializer
    from django_filters.rest_framework import DjangoFilterBackend
    from rest_framework.filters import OrderingFilter
    
    # 过滤字段使用
    class BookView(ListAPIView):
        authentication_classes = []
        permission_classes = []
        # filter_backends = [DjangoFilterBackend]
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        filter_fields = ('name', 'price')  # 配置可以按照哪个字段来过滤
    
    # 排序组件使用
    class BookView2(ListAPIView):
        authentication_classes = []
        permission_classes = []
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        # filter_backends = [OrderingFilter]
        ordering_fields = ('id', 'price')    # 排序字段

    使用时,url后?ordering参数,参数前加-表示降序

    # 使用:
    http://127.0.0.1:8000/books2/?ordering=-price
    http://127.0.0.1:8000/books2/?ordering=price
    http://127.0.0.1:8000/books2/?ordering=-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', 'sex')
        # 因为局部配置会覆盖全局配置,所以需要重新把过滤组件核心类再次声明,
        # 否则过滤功能会失效
        filter_backends = [OrderingFilter,DjangoFilterBackend]
        ordering_fields = ('id', 'age')

    5 异常处理

    异常处理用于统一接口返回

    REST framework提供了异常处理,我们可以自定义异常处理函数。

    使用方法:

    app_auth.py,自定义异常处理

    # 异常处理
    from rest_framework.views import exception_handler
    from rest_framework.response import Response
    from rest_framework import status
    
    
    def my_exception_handler(exc, context):
        response = exception_handler(exc, context)   # 调用一下原有方法,在原有基础上加处理
        # 两种情况,一个是None,drf没有处理,丢给django的,需要我们自己处理
        # response是Response对象,里面有个data
        # print(type(exc))
    
        if not response:
            if isinstance(exc, ZeroDivisionError):
                return Response(data={'status': 777, 'msg': "除以0的错误" + str(exc)}, status=status.HTTP_400_BAD_REQUEST)
            return Response(data={'status': 999, 'msg': str(exc)}, status=status.HTTP_400_BAD_REQUEST)
        else:
            # return response
            return Response(data={'status': 888, 'msg': response.data.get('detail')}, status=status.HTTP_400_BAD_REQUEST)

    全局配置settings.py

    REST_FRAMEWORK = {
        # "DEFAULT_AUTHENTICATION_CLASSES": ["app01.app_auth.MyAuthentication", ],
        # 'DEFAULT_PERMISSION_CLASSES': [
        #     'app01.app_auth.UserPermission',
        # ],
        # 'DEFAULT_THROTTLE_CLASSES': (
        #     'rest_framework.throttling.AnonRateThrottle',
        # ),
    
        'DEFAULT_THROTTLE_CLASSES': (
            'rest_framework.throttling.AnonRateThrottle',
            'rest_framework.throttling.UserRateThrottle'
        ),
        'DEFAULT_THROTTLE_RATES': {
            'user': '10/m',
            'anon': '5/m',
        },
        'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
        'EXCEPTION_HANDLER': 'app01.app_auth.my_exception_handler',    # 异常处理
    
    }

     drf几大组件

     drf几大组件
        请求(APIView源码,Requset对象)和响应(Response,自己封装Response),
        序列化,
        视图,
        路由,
        解析器(DEFAULT_PARSER_CLASSES,全局配置,局部配置),
        响应器(DEFAULT_RENDERER_CLASSES,全局配,局部配),
        认证:校验是否登录(有内置,自定义,全局配置,局部配置)
        权限:是否有权限访问某些接口(有内置,自定义,全局配置,局部配置)
        频率:限制访问频次(有内置,自定义,全局配置,局部配置),根据用户ip,根据用户id限制
        过滤:筛选,查询出符合条件的
        排序:结果进行排序
        异常:全局异常(自定义,全局配置)
        
  • 相关阅读:
    ZOJ Problem Set–2417 Lowest Bit
    ZOJ Problem Set–1402 Magnificent Meatballs
    ZOJ Problem Set–1292 Integer Inquiry
    ZOJ Problem Set–1109 Language of FatMouse
    ZOJ Problem Set–1295 Reverse Text
    ZOJ Problem Set–1712 Skew Binary
    ZOJ Problem Set–1151 Word Reversal
    ZOJ Problem Set–1494 Climbing Worm
    ZOJ Problem Set–1251 Box of Bricks
    ZOJ Problem Set–1205 Martian Addition
  • 原文地址:https://www.cnblogs.com/baicai37/p/13281961.html
Copyright © 2011-2022 走看看