67. drf之排序_过滤_分页

1. 排序

1.1 前提

drf排序、过滤、分页只针对查询所有

1.2 drf排序介绍

[1] 概念

DRF 排序存在两种实现方案:内置 OrderingFilter 过滤器、重写 get_queryset 自定义排序。

1. OrderingFilter:标准实现,接收前端 ordering 查询参数,经过白名单校验,在数据库 ORM 层执行 order_by (),不在 Python 内存完成排序;适合常规数据库字段排序。

只有继承 GenericAPIView 的子类才能使用 OrderingFilter 过滤器。

使用OrderingFilter的前提:视图必须继承 GenericAPIView 子类。ModelViewSet、ListAPIView 均间接继承 GenericAPIView,可以直接使用;原生 APIView 没有 filter_queryset 方法,配置 filter_backends 不会自动运行排序逻辑,如需使用必须手动调用 filter_queryset。
2. 重写 get_queryset:手动解析请求参数,编写 ORM 排序逻辑;用于虚拟字段、计算字段、特殊业务规则,内置过滤器无法覆盖的场景。

OrderingFilter运行步骤:前端传递 ordering 查询参数 → 后端白名单校验过滤非法字段 → 转换为 ORM order_by () 执行数据库排序。

[2] OrderingFilter核心配置项

配置项作用
filter_backends

过滤器列表,加入OrderingFilter才开启排序,支持和其他过滤器组合

视图类中固定写法:filter_backends = [OrderingFilter]

ordering_fields

排序字段白名单,只允许列表内模型数据库字段;外部传入不在白名单的字段直接丢弃;不识别序列化器别名;生产禁止__all__

视图类中需要排序的字段:ordering_fields = ["id", "price"]

ordering 默认排序,前端不传ordering时生效;前端传参则覆盖该值

[3] 前端传参规则(查询参数名固定:ordering)

1. 字段名 → 升序:?ordering=price
2. 字段前加- → 降序:?ordering=-price
3. 逗号分隔 → 多字段排序:?ordering=-create_time,id

[4] OrderingFilter配置方式

1. 局部配置:写在视图类,仅当前视图生效
2. 全局配置:写在项目的settings.py,全部视图默认生效;视图内部可以重写filter_backends实现局部优先

[5] 约束与适用边界

1. OrderingFilter 仅支持模型真实数据库字段,序列化虚拟字段、计算字段无法直接使用。
2. 遇到内置过滤器无法实现的排序需求,需要采用重写 get_queryset 自定义排序方案。
3. 必须显式配置 ordering_fields 白名单;不配置白名单,前端排序参数直接失效,不能省略该配置。
4. 排序字段只能填写模型数据库字段,序列化器重命名后的别名不能直接用于排序。
5. 大数据场景排序务必配合分页,避免数据库全表排序引发性能问题。
6. 生产环境禁止 ordering_fields="_ _all_ _",防止开放全部字段带来的安全与性能风险。

[6] 使用步骤

(1) 导入模块

(2) 配置

全局配置

REST_FRAMEWORK = {
    "DEFAULT_FILTER_BACKENDS": [
        "rest_framework.filters.OrderingFilter",
    ]
}

局部配置:在视图类中

class BookListView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [OrderingFilter]  # 使用过滤器
    ordering_fields = ["id", "price"]  # 第一优先级按 price 升序;当 price 字段值相同的时候,再使用第二优先级id 降序;
(3) 前端使用排序规则进行排序

1.3 代码准备

app01的models.py

from django.db import models

# Create your models here.

# 1.用户表,用来写登录接口
class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    age = models.IntegerField()
    role = models.IntegerField(choices=((1, '普通用户'), (2, '纯路人'), (3, 'vip')))

# 2.用户token表,记录用户的登录情况
class UserToken(models.Model):
    # 字符总数:36 个字符(32 位十六进制 + 4 个-分隔符)
    token = models.CharField(max_length=64)
    user = models.OneToOneField(to=User, on_delete=models.CASCADE)

# 3.图书表
class Book(models.Model):
    name = models.CharField(max_length=32)
    publish = models.CharField(max_length=32)
    price = models.IntegerField()

序列化组件app01的serializer.py

from .models import Book
from rest_framework import serializers

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = '__all__'

app01的views.py

from .serializer import BookSerializer
from .models import Book
from rest_framework.generics import ListAPIView

# 查询所有
class BookView(ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

app01的urls.py

from django.urls import path
from .views import BookView
urlpatterns = [
    path('books/', BookView.as_view()),
]

项目的urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/app01/', include('app01.urls')),
]

查询所有图书,默认以id进行排序

image

1.4 使用 OrderingFilter 过滤器

[1] 数据准备

image

[2] 对查询所有图书按价格升序

app01的views.py

from .serializer import BookSerializer
from .models import Book
from rest_framework.generics import ListAPIView
from rest_framework.filters import OrderingFilter

# 查询所有
class BookView(ListAPIView):
    filter_backends = [OrderingFilter]
    ordering_fields = ['price']
    queryset = Book.objects.all()
    serializer_class = BookSerializer

image

[3] 按价格降序

app01的views.py与[2]保持不变

image

[4] 字段组合排序

先按price升序,如果price一样,再按id降序

app01的views.py

class BookView(ListAPIView):
    filter_backends = [OrderingFilter]
    ordering_fields = ['price', 'id']
    queryset = Book.objects.all()
    serializer_class = BookSerializer

image

2. 过滤

2.1 drf过滤介绍

[1] 概念

DRF 过滤组件读取 URL 查询参数,对 ORM 查询集附加筛选条件,实现接口返回结果的动态过滤。
过滤逻辑仅对 list 列表接口(即查询所有)生效;retrieve、create、update、destroy 不会触发过滤。

共三类常用过滤后端:

1. DjangoFilterBackend:依赖 django‑filter 库,支持等值、区间、模糊等各类数据库条件筛选。  第三方模块
2. SearchFilter:DRF 内置组件,用于文本关键词检索
3. OrderingFilter:DRF 内置组件,用于查询结果动态排序

[2] 原理

filter_queryset 为过滤统一入口。
原始查询集,按照 filter_backends 列表的顺序,依次交给每一个过滤后端处理。后端解析请求参数,叠加 ORM 查询条件;处理完成后的查询集,继续流转到分页、序列化环节。

配置优先级规则:

1. DEFAULT_FILTER_BACKENDS,设置项目全局默认过滤后端。
2. 视图内 filter_backends 属性,直接覆盖全局配置,不会自动合并。

[3] 过滤组件全局配置

环境准备: 运行安装命令   pip install django-filter

settings.py 全局注册与配置

INSTALLED_APPS = [
    ...
    'django_filters'
]

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
        'rest_framework.filters.OrderingFilter'
    ]
}

2.2 SearchFilter使用

[1] 概念

用途:文本关键词搜索。
固定读取 url 参数 search,与search_fields中字段的值匹配。

search_fields 多字段含义:
  search_fields = ["title", "author"]
  代表同时在 title、author 两个字段做关键词检索,前端接口仍然只用一个 search 参数,不需要传多个参数。
请求示例:
  GET /books/?search=信息论
  只要标题或者作者任意一个字段包含传入的关键词,就可以匹配成功。

查询参数名固定为 search,不会因为 search_fields 有多个字段就新增请求参数。多个字段是后端内部 OR 逻辑合并,前端只传一次 search 值。

要点:

1. search 只接收单个字符串,无法分别给 title、author 传不同关键词。
  如果业务需要:单独对标题搜 A,对作者搜 B,SearchFilter 做不到,要放到 DjangoFilterBackend 自定义 filterset 实现。
2. search_fields 写模型字段名,不要写序列化器字段名。
3. 不传 search 参数时,SearchFilter 不会追加任何条件,不影响接口。

4. SearchFilter 默认是模糊搜索,不是精确匹配。

[2] 代码

需求:过滤图书名字中含有“信”字的图书

app01的views.py

from .serializer import BookSerializer
from .models import Book
from rest_framework.generics import ListAPIView
from rest_framework.filters import SearchFilter

# 查询所有
class BookView(ListAPIView):
    filter_backends = [SearchFilter]  # 使用搜索
    search_fields = ['name']  # 设置搜索的字段
    queryset = Book.objects.all()
    serializer_class = BookSerializer

image

 

2.3 第三方模块DjangoFilterBackend使用

[1] 概念

用途:数据库条件过滤。

使用需要先安装  pip install django-filter

[2] 用法一:filterset_fields,仅支持等值精准匹配

在视图类配置 filterset_fields,填写字段,仅可做完全相等的精准匹配。

(1) 需求:精准匹配一个字段,“南洋理工出版社”,文字不可缺少

app01的views.py

from .serializer import BookSerializer
from .models import Book
from rest_framework.generics import ListAPIView
from django_filters.rest_framework.backends import DjangoFilterBackend

# 查询所有
class BookView(ListAPIView):
    filter_backends = [DjangoFilterBackend]  # 使用第三方模块
    filterset_fields = ['publish']  # 设置需要匹配的字段
    queryset = Book.objects.all()
    serializer_class = BookSerializer

精准匹配“南洋理工出版社”

image

模糊匹配失败

image

(2) 需求:精准匹配两个字段,名称和出版社

app01的views.py

class BookView(ListAPIView):
    filter_backends = [DjangoFilterBackend]  # 使用第三方模块
    filterset_fields = ['name', 'publish']  # 设置需要匹配的字段
    queryset = Book.objects.all()
    serializer_class = BookSerializer

精准匹配成功

image

模糊匹配失败

image

[3] 用法二:自定义过滤类,实现区间、模糊过滤

当需要大于、小于、包含匹配等复杂条件时,需要自定义过滤类。

需求:匹配name中带有“信”字,并且price大于30

image

自定义过滤类需要放在单独的文件中,命名为app01的filters.py

from rest_framework.filters import BaseFilterBackend

# http://127.0.0.1:8000/api/v1/app01/books/?name=信&price=30
class BookFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        """
        :param request:  HTTP 请求对象,从中获取 URL 查询参数,也就是 url 问号后面携带的参数。
        :param queryset:  视图传入的原始数据集,数据库查询的初始结果集。filter 操作不会修改原对象,每次 filter 会生成新查询集。
        :param view:  当前调用该过滤器的视图类实例,可以读取视图里面定义的属性。
        :return:
        """
        # 取出过滤条件
        name1 = request.query_params.get('name', None)
        price1 = request.query_params.get('price', None)
        if name1:
            queryset = queryset.filter(name__icontains=name1)  # 模糊匹配,字段name包含前端传来的值name1
        if price1:
            queryset = queryset.filter(price__gt=price1)  # 字段price大于前端传来的值price1
        return queryset

建议写成多个if的形式,不要写成if name1 & price1的形式,可能会触发bug

app01的views.py

from .serializer import BookSerializer
from .models import Book
from rest_framework.generics import ListAPIView
from .filters import BookFilter# 查询所有
class BookView(ListAPIView):
    filter_backends = [BookFilter]  # 使用自定义的过滤类,类中已经设置需要匹配的字段,这里无需额外设置
    queryset = Book.objects.all()
    serializer_class = BookSerializer
类型父类能否放在 filter_backends使用方式
自定义过滤器后端 BaseFilterBackend 可以 直接写进 filter_backends 列表
SearchFilter / OrderingFilter BaseFilterBackend 可以 直接写进 filter_backends 列表
DjangoFilterBackend BaseFilterBackend 可以

作为运行,配合 filterset_class

filter_backends = [DjangoFilterBackend]
filterset_class = 自定义过滤类
django‑filter.FilterSet 子类 object 不可以 视图属性 filterset_class 引用

 

业务逻辑:

  1. 从 request.query_params 读取 url 上的 name、price 两个查询参数,取不到时赋值 None。
  2. 如果 url 携带 name 参数:执行 name__icontains 过滤,做不区分大小写的模糊包含匹配。
  3. 如果 url 携带 price 参数:执行 price__gt 过滤,匹配价格大于传入数值的数据。
  4. 返回经过叠加过滤之后的 queryset。

可以实现的接口效果:

请求 URL执行效果
/books/ 不传参数,返回全部书本数据
/books/?name = 信 查询书本名称包含信,大小写不敏感的全部记录
/books/?price=30 查询价格大于 30 的全部书本记录
/books/?name = 信 & price=30 同时生效:名称模糊匹配信,并且价格大于 30,两个条件为并且关系

关键点:

  1. price 从 url 取出来是字符串类型,DRF 内部 filter 会自动完成类型转换;传入非数字字符串,会直接抛出数据库异常。
  2. 多个 if 独立判断,参数可以单独使用,也可以组合使用;没有参数时直接返回原始 queryset,不会过滤。

不传参数,返回全部书本数据

image

查询书本名称包含信,大小写不敏感的全部记录

image

查询价格大于 30 的全部书本记录

image

名称模糊匹配信,并且价格大于 30,两个条件为并且关系

image

2.4 过滤和排序组合使用

filter_backends 列表填写多个后端,按列表顺序依次运行过滤逻辑。

自定义过滤器app01的filters.py

from rest_framework.filters import BaseFilterBackend

# http://127.0.0.1:8000/api/v1/app01/books/?name=信&price=60
class BookFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        """
        :param request:  总数据
        :param queryset:  当次请求对象
        :param view:  视图类对象
        :return:
        """
        # 取出过滤条件
        name1 = request.query_params.get('name', None)
        price1 = request.query_params.get('price', None)
        if name1:
            queryset = queryset.filter(name__icontains=name1)  # 模糊匹配,字段name包含前端传来的值name1
        if price1:
            queryset = queryset.filter(price__gt=price1)  # 字段price大于前端传来的值price1
        return queryset

app01的views.py

from .serializer import BookSerializer
from .models import Book
from rest_framework.generics import ListAPIView
from .filters import BookFilter
from rest_framework.filters import OrderingFilter, SearchFilter

# 查询所有
class BookView(ListAPIView):
    filter_backends = [BookFilter, SearchFilter, OrderingFilter]  # 使用自定义的过滤类,类中已经设置需要匹配的字段,这里无需额外设置
    search_fields = ['name', 'publish']
    ordering_fields = ['price', 'id']
    queryset = Book.objects.all()
    serializer_class = BookSerializer

概念说明:

  自定义 BookFilter 实现 name 模糊匹配、price 大于条件过滤。
  SearchFilter 依靠 search_fields 配置,单个 search 查询参数,对配置内全部字段执行或逻辑模糊查询。
  OrderingFilter 依靠 ordering_fields 配置,支持逗号分隔多字段排序,可同时使用配置列表内全部字段,负号代表降序。

  filter_backends 执行顺序:BookFilter → SearchFilter → OrderingFilter。
  运行逻辑为先过滤数据集,再做关键词检索,最后执行排序。

 接口:

  GET /books/?name = 信 & price=20&search = 麻省理工出版社 & ordering=price,-id

参数作用
name = "信" 自定义过滤器,name 字段大小写不敏感模糊包含"信"
price=20 自定义过滤器,筛选价格大于20
search = 麻省理工出版社 SearchFilter,同时检索 name、publish 字段,命中任意一个即匹配
ordering=-price,id OrderingFilter,优先 price升序,价格相同则id降序,两个排序字段全部启用

运行步骤:
  1. 获取 Book 全部原始查询集。
  2. BookFilter 执行过滤,保留 name 包含"信"、price 大于20的数据子集。
  3. SearchFilter 在上一步子集内筛选,匹配 name 或 publish 包含麻省理工出版社的记录。
  4. OrderingFilter 执行多字段排序。
  5. BookSerializer 序列化输出结果。

image

image

2.5 练习

需求:继承APIView实现过滤

方式一:

app01的views.py

from rest_framework.views import APIView
class BookViewSet(APIView):
    def get(self, request, *args, **kwargs):
        obj_list = Book.objects.all()
        # 取出需要过滤的字段
        name = request.query_params.get('name')
        price = request.query_params.get('price')
        if name:
            obj_list = obj_list.filter(name=name)
        if price:
            obj_list = obj_list.filter(price=price)
        serializer = BookSerializer(obj_list, many=True)
        return Response(serializer.data)

基于 APIView 实现自定义查询接口,接收 GET 请求,完成图书列表查询,同时支持 name、price 两个 URL 查询参数做等值过滤,返回序列化后的图书 JSON 列表数据。

运行步骤:
  1. 接收 HTTP GET 请求进入 get 方法。
  2. 查询全部 Book 表数据作为初始查询集。
  3. 从 url 查询参数中提取 name 与 price。
  4. 参数存在时,对查询集执行对应字段等值过滤。
  5. 使用 BookSerializer 对结果做多对象序列化。
  6. 将序列化数据通过 Response 返回。

接口示例
示例 1:获取全部图书
GET /xxx/books
示例 2:按图书名称精确匹配过滤
GET /xxx/books?name = 信息论
示例 3:按价格精确匹配过滤
GET /xxx/books?price=39
示例 4:两个参数同时生效,相当于 AND 条件
GET /xxx/books?name = 信息论 & price=39

关键点:
1. 仅支持 GET 请求,没有post、put、delete,不具备增删改。
2. 过滤为完全等值匹配,不支持模糊查询、大于小于区间筛选。

方式二:

过滤组件app01的filters.py

from rest_framework.filters import BaseFilterBackend

# http://127.0.0.1:8000/api/v1/app01/books/?name=信&price=60
class BookFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        """
        :param request:  总数据
        :param queryset:  当次请求对象
        :param view:  视图类对象
        :return:
        """
        # 取出过滤条件
        name1 = request.query_params.get('name', None)
        price1 = request.query_params.get('price', None)
        if name1:
            queryset = queryset.filter(name__icontains=name1)  # 模糊匹配,字段name包含前端传来的值name1
        if price1:
            queryset = queryset.filter(price__gt=price1)  # 字段price大于前端传来的值price1
        return queryset

app01的views.py

from rest_framework.views import APIView
from .filters import BookFilter

class BookViewSet(APIView):
    def get(self, request, *args, **kwargs):
        obj_list = Book.objects.all()
        # 取出需要过滤的字段
        obj_list = BookFilter.filter_queryset(request, obj_list, self)
        serializer = BookSerializer(obj_list, many=True)
        return Response(serializer.data)

自定义 BaseFilterBackend 过滤器类,在 APIView 视图中手动调用过滤器方法,实现 GET 请求下对图书列表的过滤。name 运行不区分大小写模糊匹配,price运行大于数值过滤,返回过滤完成后的序列化图书列表。

运行步骤:
  1. 接收 GET 请求进入 get 方法。
  2. 获取 Book 表全部数据作为原始查询集。
  3. 手动调用 BookFilter 的 filter_queryset 静态方式,传入 request、查询集、视图实例。
  4. 读取 url 查询参数 name1、price1。
  5. name1 存在:使用 name__icontains 做不区分大小写模糊检索。
  6. price1 存在:使用 price__gt 做价格大于传入值的过滤。
  7. 返回处理后的查询集交给序列化器输出 JSON 数组。

接口示例
  示例 1:全部数据
    GET /books/
  示例 2:名称模糊匹配,不区分大小写
    GET /books/?name1 = 信
    匹配:信息论、通信原理等包含 “信” 的记录。
  示例 3:价格大于指定数值
    GET /books/?price1=30
    返回所有 price>30 的图书。
  示例 4:两个参数组合,逻辑 AND
    GET /books/?name1 = 三 & price1=30
    返回名称含 “三” 并且价格大于 30 的数据。

关键特点

1. BookFilter 继承 BaseFilterBackend,是 DRF 标准过滤器基类,但此处没有使用视图 filter_backends 配置,属于手动调用过滤器函数,不是 DRF 原生自动调用机制。
2. 过滤条件逻辑为 AND,多参数同时提供时条件叠加。

和方式一的区别

1. name:等值匹配 → icontains 模糊不区分大小写。
2. price:等于匹配 → gt 大于过滤。
3. 过滤逻辑抽成 BaseFilterBackend 过滤器类,但手动调用,没有利用 GenericAPIView 的过滤器自动执行机制。

3. 分页

3.1 概念

[1] 基础概念

drf分页只针对于查询所有

手机app端:下拉加载下一页

web端:点击加载下一页

[2] 三种分页方式

DRF 分页提供三种内置分页类,分别为普通分页、偏移分页、游标分页,支持全局配置与视图局部配置两种配置方式。

分页类 类路径分页逻辑适用场景
PageNumberPagination 普通分页 rest_framework.pagination.PageNumberPagination 传递 page 参数,按页码分页 简单列表,支持跳转到任意页
LimitOffsetPagination 偏移分页 rest_framework.pagination.LimitOffsetPagination 传递 limit、offset 参数,从偏移位置取数据 需要做数据偏移,适合前端滚动加载
CursorPagination 游标分页 rest_framework.pagination.CursorPagination 传递 cursor 游标,基于排序字段分页 大数据量,不支持跳页,避免分页数据重复丢失

[3] 使用步骤

定义分页类,继承三个分页类之一

在分页类中,重写类中的某些函数

在继承GenericAPIView的子类中,配置pagination_class = CommonCursorPagination

3.2 代码示例

[1] 普通分页--自定义

(1) 代码

分页类需要放在单独的文件中,定义为app01的page.py

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

class TurnPage(PageNumberPagination):
    page_size = 2  # 每页显示2条
    page_query_param = 'page'  # 页码参数名,值为page时,接口为/books/?page=3  值为p时,接口为/books/?p=3,都是访问第3页
    page_size_query_param = 'size'  # 值定义为size时,/books/?page=6&size=10,查询的第6页并且将原来默认每页显示2条动态调整为10条,所有页数都是10条,不止第6页
    max_page_size = 5  # 通过以上size指定时,最多只显示5条

  page_size:默认每页条数
  page_size_query_param:允许前端传参动态修改每页数量的参数名
  max_page_size:前端可请求的最大单页条数,防止一次性请求大量数据
  page_query_param:页码请求参数名

app01的views.py

from .serializer import BookSerializer
from .models import Book
from rest_framework.generics import ListAPIView
from .page import TurnPage

# 查询所有
class BookView(ListAPIView):
    pagination_class = TurnPage
    queryset = Book.objects.all()
    serializer_class = BookSerializer

(2) 接口示例

http://127.0.0.1:8000/api/v1/app01/books/?page=1  查询第1页,每页返回默认2条数据

http://127.0.0.1:8000/api/v1/app01/books/?page=2  查询第2页,每页返回默认2条数据

http://127.0.0.1:8000/api/v1/app01/books/?page=2&size=4  查询第2页,返回4条数据,查询所有的页数都将返回4条,不止第2页  

http://127.0.0.1:8000/api/v1/app01/books/?page=1&size=60000  查询第1页,按max_page_size最多只显示5条

(3) 接口测试

查询所有,总共5条,每页显示2条,下一页为第2页

image

查询第2页,下一页为第3页

image

查询第2页,每页条数动态修改为3页

动态修改后第1页显示的条数为1、2、3,因此第2页只能显示剩下的4、5

image

当第2页超过max_page_size时,第1页已经显示max_page_size = 5的5条,第2页已经查询不到数据

image

当第1页超过max_page_size时,第1页显示max_page_size = 5的5条,获取到了所有数据

image

[2] 偏移分页

app01的views.py无需改变,app01的page.py如下

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

class TurnPage(LimitOffsetPagination):
    # 参数在源码中
    default_limit = 2  # 每页默认显示2条
    limit_query_param = 'limit'
    offset_query_param = 'offset'
    max_limit = 5  # 每页最多显示5条

自定义类核心参数解释

  default_limit:默认单页返回条数,等价全局 PAGE_SIZE。
  limit_query_param:前端控制每页条数参数名,默认 limit。
  offset_query_param:前端控制偏移量参数名,默认 offset。    偏移量从0开始,根据偏移量和limit计算页码
  max_limit:前端允许设置的最大 limit,限制单次返回数据量。

查询所有,每页最多按limit显示2条

image

偏移量offset=2,从计数3开始,按limit每页显示2条

image

偏移量offset=4,从计数5开始,按limit每页显示2条,这里已经显示最后一条

image

偏移量offset=2,从计数3开始,按limit每页显示3条

image

[3] 游标分页 

(1) 概念

先排序,后分页,只能前一页和后一页
必须提供排序字段,无总 count 返回不支持指定页码跳转。

(2) 代码

app01的page.py

from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination

class TurnPage(CursorPagination):
    # 参数在源码中
    page_size = 2  # 每页默认显示2条
    cursor_query_param = 'cursor'
    ordering = 'id'  # 分页的依据

(3) 参数解释

page_size:默认每页条数。
cursor_query_param:前端传递游标字符串的参数名,默认 cursor。
ordering:必填,数据库排序字段,优先使用唯一主键,字段不唯一会造成数据错乱。
page_size_query_param:允许前端动态修改每页条数。
max_page_size:前端可设置最大每页条数上限。

(4) 接口测试

查询所有,按page_size每页默认显示2条

image

根据生成的cursor参数进行上一页与下一页的跳转

image

优点:大数据量翻页性能优秀;新增删除数据不会出现重复漏数据。

缺点:没有总条数 count;不能跳页;排序字段尽量使用唯一主键,否则会错乱。

 

posted @ 2026-08-31 02:36  pythondjango  阅读(8)  评论(0)    收藏  举报