首页 > 其他分享 >drf之三大认证、过滤、排序、分页组件

drf之三大认证、过滤、排序、分页组件

时间:2023-02-07 21:46:19浏览次数:46  
标签:之三 request 认证 filter token user 组件 class drf

drf之三大认证、过滤、排序、分页组件

本文所介绍的组件,都有着很相似的配置方式,继承组件类,类体中配置参数,视图类中配置参数添加对应的组件类或者全局配置,我们就可以方便的使用drf提供的组件了。这些组件也足够常用。

认证组件

对于接口而言,有些接口应该是需要登录认证后才能访问,我们需要先写一个登录的功能:

登录接口

登录功能我们首先要简单的准备用户模型表:

  • 用户表存储用户信息,这里就只用用户和密码两个字段了
  • token表相当于我们自己建立的session表,在服务端存一个随机字符串,用于两侧比对验证登录。

登录的路由和视图类一并配好,因为login功能不是五个常用接口之一,所以需要加action装饰器,书写路由时则按/api/v1/user/login的 post 请求。

login接口的逻辑也是需要自己重写的:

  1. 验证用户名和密码,查看是否有这个用户
  2. 有相应用户的话拿到token表中比对,如果有的话更新token值即可,如果没有则新建一条
#### 表模型

class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)

class UserToken(models.Model):  # 跟User是一对一
    token = models.CharField(max_length=32)
    user = models.OneToOneField(to='User', on_delete=models.CASCADE, null=True)
    # user :反向,表名小写,所有有user字段

### 路由
router.register('user', views.UserView, 'user')  # /api/v1/user/login     post 请求

# 视图类
####  登录接口  自动生成路由+由于登录功能,不用序列化,继承ViewSet
from .models import User, UserToken
import uuid


class UserView(ViewSet):
    @action(methods=['POST'], detail=False)
    def login(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        if user:
            # 用户存在,登录成功
            # 生成一个随机字符串--uuid
            token = str(uuid.uuid4())  # 生成一个永不重复的随机字符串
            # 在userToken表中存储一下:1 从来没有登录过,插入一条,     2 登录过,修改记录
            UserToken.objects.update_or_create(user=user, defaults={'token': token})
            return Response({'code': '100', 'msg': '登录成功', 'token': token})
        else:
            return Response({'code': '101', 'msg': '用户名或密码错误'})

update_or_create的用法,按照defaults外的字段判断是更新还是新增,defaults的内容一定会更新到表中。

认证功能实现

需求:查询所有登不登录都可以,查询单个需要登录

认证功能流程:

  1. 继承BaseAuthentication类,产生认证子类

  2. 在子类中必须覆写authenticate(self, request)方法

    • 方法中必须固定的返回(用户数据对象,token值)

      这里的用户数据对象会保存在request.user中

    • 如果认证不成功则需要抛出指定的异常rest_framework.exceptions.AuthenticationFailed

# 认证类
from rest_framework import authentication
from .models import UserToken
from rest_framework.exceptions import AuthenticationFailed

class CommonAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        token = request.query_params.get('token')
        if not token:
            raise AuthenticationFailed('没有传token')
        user_token = UserToken.objects.filter(token=token).first()
        if not user_token:
            raise AuthenticationFailed('你还未登录,无法使用接口权限')
        return user_token.user, token
  1. 将认证子类注册到局部或全局

    • 局部注册--在继承自GenericAPIView的视图类内部配置参数

      from .authentication import CommonAuthentication
      
      class BookDetailView(GenericViewSet, mixins.RetrieveModelMixin):
          queryset = Book.objects.all()
          serializer_class = BookSerializer
          # 还可以配置多个认证类,从左到右执行
          authentication_classes = [CommonAuthentication]
      
    • 全局注册

      # 项目的settings.py中
      REST_FRAMEWORK = {
          'DEFAULT_AUTHENTICATION_CLASSES': [
              'app01.authentication.CommonAuthentication'
          ],
      }
      
    • 局部禁用--在继承自GenericAPIView的视图类内部的认证类配置为空列表

      class UserView(GenericViewSet):
          authentication_classes = []
      

权限组件

需求:用户为超级管理员则可以新增,修改等,普通用户只能查看单个,没有登录同上

这就涉及了权限问题,认证后也不一定拥有相应的权限,drf也提供了权限组件。

数据准备

在用户模型表增加一个权限字段,这里就设置为用户类型好了:

user_type = models.IntegerField(choices=((1, '超级管理员'), (2, '普通用户')), default=2)

迁移好后,那么在后续判断权限时就可以通过这个字段来判断。

这里用到了choices列举此字段的所有可能,存储和正常取出都是1,2数字,但是通过get_字段_display可以拿到超级管理员、普通用户数据。

权限功能实现

权限功能流程:

  1. 继承BasePermission类,产生子类

  2. 在类中改写has_permission方法

    在得到有权限的结论时返回True,没有则返回False,可以使用self.message更改无权限的提示信息

    class CommonPermission(BasePermission):
        def has_permission(self, request, view):
            if request.user.user_type == 1:
                return True
            self.message = f'你是{request.user.get_user_type_display()},你的权限不够'
            return False
    
  3. 将权限类校验注册到局部或全局生效。

    # 在视图类中局部配置
    class BookDetailView(GenericViewSet, mixins.RetrieveModelMixin):
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        permission_classes = [CommonPermission]  # 需要登录认证后再进行权限认证
        
    # 全局配置
    REST_FRAMEWORK = {
        'DEFAULT_PERMISSION_CLASSES': [
            'app01.permission.CommonPermission',
        ],
    }
    

频率组件

用于控制某接口的访问频率,如查询所有对于同一个ip,限制它只能一分钟访问5次。

限制频率功能的简单实现

  1. 继承SimpleRateThrottle类写一个频率类

  2. 重写get_cache_key方法,返回什么,就以什么做限制(可以是ip或者用户id)

  3. 配置一个类属性:scope='book_5_m'

    from rest_framework.throttling import SimpleRateThrottle
    
    class CommonThrottle(SimpleRateThrottle):
        scope = 'book_5_m'
    
        def get_cache_key(self, request, view):
            return request.META.get('REMOTE_ADDR')
    
  4. 在配置文件中配置 'DEFAULT_THROTTLE_RATES': {'book_5_m': '5/m',},

    REST_FRAMEWORK = {
        'DEFAULT_THROTTLE_RATES': {
            'book_5_m': '5/m',
        },
    }
    
  5. 局部使用和全局使用

    • 局部使用--直接在视图类中添加throttle_classes列表参数,将频率类放入。

      class BookView(GenericViewSet, mixins.ListModelMixin):
          queryset = Book.objects.all()
          serializer_class = BookSerializer
          authentication_classes = []
          permission_classes = [CommonPermission]
          throttle_classes = [CommonThrottle]
      
    • 全局使用--配置settings

      REST_FRAMEWORK = {
          'DEFAULT_THROTTLE_CLASSES': [
              'app01.throttle.CommonThrottle',
          ],
      }
      

    三大认证执行顺序:

    每一种认证都按局部先生效,再是项目的settings.REST_FRAMEWORK,最后是drf的settings.DEFAULTS

    三大认证按authentication--》permission--》throttle的顺序执行。

过滤排序组件

在restful规范中,要求请求地址携带过滤条件

而5大接口中,只有查询所有需要携带过滤条件。

内置过滤类SearchFilter的使用

SearchFilter是固定用法,会模糊匹配所有字段。

from rest_framework.filters import SearchFilter

class BookView(GenericViewSet, mixins.ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    
    filter_backends = SearchFilter

http://127.0.0.1:8001/api/v1/book/?search=红会模糊搜索所有序列化字段,包括name、price、publisher。

使用第三方djagno-filter实现过滤

首先需要安装djagno-filter,它的规则是完整的匹配字段的内容,而且搜索携带的数据必须按照:?字段=内容&字段=内容

class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['name','price']  # 定制了可以通过name和price字段进行完整匹配查询

自定制过滤类实现过滤

这种能够自定义通过什么样的字段进行匹配查询

定义过滤类,继承BaseFilterBackend,重新filter_queryset方法

然后将过滤类,配置到视图函数中,还是通过filter_backends = []的方式

# 过滤类
from rest_framework.filters import BaseFilterBackend
from django.db.models import Q


class CommonFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        q_obj = Q()
        # 遍历查询参数,将参数加到q条件中,进行filter查询
        for field, condition in request.query_params.items():
            if field in view.filter_fields:  # 支持视图类中对查询字段进行编辑
                q_obj.children.append((field,condition))
        return queryset.filter(q_obj)
    
# 视图类
class BookView(GenericViewSet, mixins.ListModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    authentication_classes = []
    throttle_classes = [CommonThrottle]
    filter_backends = [CommonFilter]
    filter_fields = ['price__contains', 'name__contains']

排序类

排序使用内置的即可,它也属于过滤类的一种,配置在filter_backends列表中即可。

class BookView(ViewSetMixin, ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [OrderingFilter]  
    ordering_fields = ['price']   # 支持排序的字段

配置好后前端可以通过以下方式进行查询:

http://127.0.0.1:8001/api/v1/book/?ordering=price   # 正常按价格排序
http://127.0.0.1:8001/api/v1/book/?ordering=-price   # 倒序排序
http://127.0.0.1:8001/api/v1/book/?ordering=-id,price   # 先按一个字段排序,如果有相同的再按另外一个字段排序

分页类

分页只有查询所有接口需要。drf内置了三个分页器,对应三种分页方式。

继承PageNumberPagination

这种最贴合人对分页的习惯,但是效率并不算高,它只需要设置以下参数:

from rest_framework.pagination import PageNumberPagination

class CommonPageNumberPagination(PageNumberPagination):
    page_size = 2  # 每页显示2条
    page_query_param = 'page'  # 前端分页键的名字,?page=10 查询第十页
    page_size_query_param = 'size'  # ?page=10&size=5    查询第10页,每页显示5条
    max_page_size = 5  # 每页最大显示5条,size的值大于这个值无效,只取5

而在前端拿到了下面的数据:

{
    "count": 3,
    "next": "http://127.0.0.1:8001/api/v1/book/?page=2",
    "previous": null,
    "results": [...]
}

分别对应总数据条数,下页的数据网址,上页的数据网址,数据结果。

前端的网址可以通过携带?page=页数来拿到指定页的数据。

继承LimitOffsetPagination

这种则是让前端通过limit每页条数和offset偏移条数两个参数进行分页,即开始的条数和此页有几条数据的方式取。

网址方式:http://127.0.0.1:8001/api/v1/book/?offset=2&limit3

class CommonLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 3  # 每页显示2条
    limit_query_param = 'limit'  # limit=3   取3条
    offset_query_param = 'offset'  # offset=1  从第一个位置开始,取limit条
    max_limit = 5

继承CursorPagination

这种分页方式只支持上下翻页,但是面对大量数据,其效率会有很大的提升。

class CommonCursorPagination(CursorPagination):
    cursor_query_param = 'cursor'  # 查询参数
    page_size = 2  # 每页多少条
    ordering = 'id'  # 排序字段

标签:之三,request,认证,filter,token,user,组件,class,drf
From: https://www.cnblogs.com/Leethon-lizhilog/p/17099892.html

相关文章

  • drf-认证、权限、频率、过滤、排序、分页
    1.认证组件1.1局部认证1.首先写两个接口,一个查询单个一个查询所有,我们利用视图扩展类和视图子类写在一个视图类上:views.py:fromrest_framework.viewsetsimportVie......
  • drf从入门到精通 07
    今日内容详细认证组件有些接口需要登录才能够访问有些不需要登录就可以直接访问由此引出来了认证组件需求查看所有和创建无需登陆其他需要登录先写一个登录接口......
  • drf从入门到飞升仙界 06
    模型类准备fromdjango.dbimportmodels#图书跟作者:多对多,需要建立中间表,但是我们可以通过ManyToManyField自动生成,写在哪里都行#图书跟出版社:一对多,一个出版社,出......
  • vue高级进阶( 一 ) 组件精髓概述
    前言这个系列可能会分为几部分:基础以及高级用法总结一些比较有代表性的实战源码解析(一定是用最粗俗,不对,是最通俗的语言讲解,这个我可以保证)总之一定对得起高级......
  • vue高级进阶( 二 ) 8种组件通信详解
     vue高级进阶(二)8种组件通信详解猛兽总是独行,牛羊才成群结队。-------鲁迅vue组件通信的重要性无需多言。。。但是你肯定没有全部掌握,所以这第二篇文章应运而......
  • vue高级进阶( 三 ) 组件高级用法及最佳实践
     vue高级进阶(三)组件高级用法及最佳实践世界上有太多孤独的人害怕先踏出第一步。---绿皮书书接上回,上篇介绍了vue组件通信比较有代表性的几种方法,本篇主要讲......
  • drf7
    今日内容概要认证组件权限组件频率组件过滤排序分页今日内容详细认证组件有些接口需要登录才能够访问有些不需要登录就可以直接访问由此引出来了认证组件需......
  • 认证组件、权限组件、频率组件、过滤排序、分页
    认证组件、权限组件、频率组件、过滤排序、分页认证组件1.认证组件###视图fromrest_framework.genericsimportListAPIView,RetrieveAPIViewfromrest_framework.m......
  • python之路65 drf从入门到成神 5 认证组件、权限组件、频率组件、过滤排序、分页
    认证组件以后,有的接口需要登录后才能访问,有的接口,不登录就能访问写一个登录接口,返回token,以后只要带着token过来,就是登录了不带就没有登录需求:查询所有不需要登录......
  • drf
    今日内容1.认证组件2.权限组件3.频率组件4.过滤排序5.分页1.认证组件认证组件使用步骤1.写一个认证类,继承BaseAuthentication2.重写authenticate方法,在该方法在中......