DRF学习06-三大认证、过滤排序、分页

DRF三大认证、过滤排序、分页笔记

执行顺序:APIView的dispatch() → initial()方法内部依次执行 认证 → 权限 → 频率,在视图get/post方法执行之前运行。

一、APIView的dispatch源码流程(底层原理)

def dispatch(self, request, *args, **kwargs):
    # 1.封装生成新的request对象
    request = self.initialize_request(request, *args, **kwargs)
    self.request = request
    try:
        # 【核心】三大认证在这里执行
        self.initial(request, *args, **kwargs)
            # self.perform_authentication(request)  # 认证
            # self.check_permissions(request)        # 权限
            # self.check_throttles(request)          # 频率

        # 反射:根据请求方式(get/post)找到视图类对应的方法
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        response = handler(request, *args, **kwargs)

    except Exception as exc:
        # 全局异常捕获,统一处理返回
        response = self.handle_exception(exc)

    return self.finalize_response(request, response, *args, **kwargs)

重要结论

  1. 继承APIView后,request被重新封装,视图内可以使用self.request
  2. 视图方法执行前,先跑认证、权限、频率校验
  3. 整个过程抛出的异常都会被DRF捕获处理
  4. APIView自动关闭CSRF校验

1、认证 Authentication(判断用户是否登录)

作用:接口需要登录之后才可以访问。token登录方案。

流程

  1. 登录接口:用户名密码校验,生成token,存入数据库中间表UserToken
  2. 访问需要登录的接口:请求头携带token
  3. 自定义认证类读取请求头token,去数据库校验
  4. 校验成功返回 (user对象, token);失败抛AuthenticationFailed异常

①登录接口示例(ViewSet + @action)

from rest_framework.viewsets import ViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
import uuid
from .models import User, UserToken

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:
            token_str = str(uuid.uuid4())
            # 有就更新,没有就新增
            UserToken.objects.update_or_create(user=user, defaults={"token": token_str})
            return Response({"code":100, "msg":"登录成功", "token":token_str})
        return Response({"code":101, "msg":"用户名或密码错误"})

②自定义认证类

继承 BaseAuthentication,重写authenticate方法

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import UserToken

class LoginAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 从请求头拿token,请求头key:Token → META中变成 HTTP_TOKEN
        token = request.META.get("HTTP_TOKEN")
        user_token = UserToken.objects.filter(token=token).first()
        if user_token:
            # 返回 (用户对象, token),后续视图可以通过 request.user 获取登录用户
            return user_token.user, token
        raise AuthenticationFailed("请登录后操作")

③使用认证类(3种写法)

  1. 局部视图使用(只当前视图生效)
class BookView(CreateAPIView):
    authentication_classes = [LoginAuthentication]
  1. 全局配置(settings.py) 所有视图默认生效
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "app.auth.LoginAuthentication"
    ]
}
  1. 局部禁用(比如登录接口不需要登录)
authentication_classes = []

查找优先级:视图类局部 > settings全局 > drf内置


2、权限 Permissions(已经登录,判断用户有没有操作资格)

场景:普通用户可以查,只有超级管理员才能新增/修改数据

使用步骤

  1. 继承BasePermission
  2. 重写 has_permission(self, request, view)
  3. 有权限返回True;没有权限返回False;可以设置self.message自定义错误提示
from rest_framework.permissions import BasePermission

class SuperPermission(BasePermission):
    def has_permission(self, request, view):
        # request.user 就是认证类返回的登录用户
        if request.user.user_type == 3:
            return True
        self.message = "您不是超级用户,无法操作"
        return False

使用方式

  1. 局部配置
class BookView(CreateAPIView):
    permission_classes = [SuperPermission]
  1. 全局配置 settings.py
REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": [
        "app.permission.SuperPermission"
    ]
}
  1. 局部禁用
permission_classes = []

3、频率 Throttling(限流,限制访问次数,防刷接口)

根据IP / 用户id /设备id限制一段时间内访问次数

使用步骤

  1. 继承SimpleRateThrottle
  2. 设置类属性 rate = '3/m' → 每分钟最多3次
    • s秒,m分钟,h小时,d天
  3. 重写get_cache_key,返回用来做限制的标识(ip、用户id)
from rest_framework.throttling import SimpleRateThrottle

class CommonThrottle(SimpleRateThrottle):
    rate = "3/m"  # 一分钟最多访问3次
    def get_cache_key(self, request, view):
        # 根据客户端IP限流(不需要登录)
        return request.META.get("REMOTE_ADDR")

        # 如果登录用户,按用户id限流
        # return request.user.id

使用

#局部
throttle_classes = [CommonThrottle]

#全局 settings.py
REST_FRAMEWORK={
    "DEFAULT_THROTTLE_CLASSES":["app.throttle.CommonThrottle"]
}
#局部禁用
throttle_classes = []

底层使用Django缓存保存访问记录。


4、过滤与排序(只针对查询列表接口 ListAPIView / ModelViewSet的list)

需要继承GenericAPIView的视图才支持filter_backends

4.1 排序 OrderingFilter

from rest_framework.filters import OrderingFilter

class BookView(ModelViewSet):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [OrderingFilter]
    ordering_fields = ["price", "id"] # 允许排序的字段

访问地址:

  • ?ordering=price 升序
  • ?ordering=-price 降序
  • ?ordering=price,-id 多字段排序

4.2 搜索(drf内置SearchFilter,模糊匹配)

from rest_framework.filters import SearchFilter
filter_backends = [OrderingFilter, SearchFilter]
search_fields = ["name"]

请求:?search=红楼梦

4.3 第三方精确过滤 django‑filter

  1. 安装 pip install django-filter,注册app
from django_filters.rest_framework.backends import DjangoFilterBackend

filter_backends = [DjangoFilterBackend]
filterset_fields = ["name", "publish"]

请求:?name=红楼梦&publish=北京出版社

4.4 自定义过滤类(最灵活,可以写Q复杂查询)

继承BaseFilterBackend,重写filter_queryset

from rest_framework.filters import BaseFilterBackend
from django.db.models import Q

class CommonFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        name = request.query_params.get("name")
        if name:
            queryset = queryset.filter(Q(name__contains=name))
        return queryset

视图配置

filter_backends = [CommonFilter]

✔排序、搜索、自定义过滤可以同时写在filter_backends列表里,组合使用。


5、分页(只作用于查询全部list接口)

DRF提供3套分页器,全部继承GenericAPIView视图配置 pagination_class = 分页类

① PageNumberPagination(最常用,页码分页)

url格式:?page=2&size=3

from rest_framework.pagination import PageNumberPagination

class CommonPageNumberPagination(PageNumberPagination):
    page_size = 2               # 默认每页2条
    page_query_param = "page"   # 页码参数名
    page_size_query_param = "size" # 用户自定义每页条数
    max_page_size = 5           # 最大不能超过5条

② LimitOffsetPagination(偏移分页)

url格式:?offset=4&limit=2
offset:跳过多少条;limit:取多少条

class CommonLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 2
    limit_query_param = "limit"
    offset_query_param = "offset"
    max_limit =5

③ CursorPagination(游标分页,适合大数据,不能跳页,只能上一页下一页)

必须指定排序字段,一般按id

class CommonCursorPagination(CursorPagination):
    cursor_query_param = "cursor"
    page_size =2
    ordering = "id"

在视图上启用分页

class BookView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    pagination_class = CommonPageNumberPagination

快速复习总结表

模块 父类 核心方法 作用
认证 BaseAuthentication authenticate 判断是否登录,返回user
权限 BasePermission has_permission 判断登录用户有没有操作权限
频率 SimpleRateThrottle get_cache_key 限制访问次数,防刷接口
过滤排序 filter_backends filter_queryset 对列表数据筛选、排序
分页 三种Pagination — 大数据列表分页返回

执行顺序:认证 → 权限 → 频率 → 执行视图的list/get/post方法
过滤、排序、分页只在查询全部(list)的时候生效。

如果你想,我可以基于你现有的stu学生app,写一套完整实操案例,把认证、权限、限流、过滤、分页全部跑一遍。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容