【11.0】DRF之过滤排序分页

【准备数据】

  • 模型
from django.db import models


# Create your models here.
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.IntegerField()
  • 序列化类
# -*-coding: Utf-8 -*-
# @File : book_serializer .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/7/30
from app01 import models
from rest_framework import serializers


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

【一】过滤

  • 在RESTful规范中

    • 当我们需要使用过滤条件来查询资源时,可以将过滤条件作为请求地址的一部分来实现
    • 通常,在RESTful API中,有一个用于查询所有资源的接口,可以使用过滤条件对这些资源进行筛选。
  • 例如

    • 考虑一个资源为用户的API,我们可以使用以下方式来实现带过滤条件的查询所有用户的接口:
    GET /users?filter=条件
    
    • /users是资源的基本路径,filter=条件表示过滤条件。
  • 带过滤的接口只有:查询所有

【1】使用

  • 搜索某个字段中带有某个参数

内置过滤类

# (2) 过滤:必须继承 GenericAPIView 及其子类,才能使用(如果继承APIView,则不能这么写)
# DRF 给我们提供了一个排序类
from rest_framework.filters import SearchFilter


class BookView(GenericViewSet, ListModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [SearchFilter]  # 排序类
    # SearchFilter:必须配合一个类属性
    # 按照那些字段进行筛选
    # http://127.0.0.1:8000/app01/v1/books/?search=梦
    # 只要name/price中带 梦 都能被搜出来
    search_fields = ['name']
  • from rest_framework.filters import SearchFilter
    • 导入DRF中的SearchFilter过滤器类,该类用于进行搜索过滤。
  • GenericViewSetListModelMixin
    • 这两个类是DRF提供的视图集和列表模型混合类,用于创建支持列表操作的视图。
  • queryset = models.Book.objects.all()
    • 定义查询集,即需要进行搜索的资源对象集合。
  • serializer_class = BookSerializer
    • 定义序列化器类,用于序列化和反序列化数据。
  • filter_backends = [SearchFilter]
    • 指定过滤器后端,将SearchFilter添加到过滤器列表中。
  • search_fields = ['name']
    • 指定进行搜索过滤的字段,这里以name字段作为搜索条件。
  • http://127.0.0.1:8000/app01/v1/books/?search=梦

    [
        {
            "id": 5,
            "name": "小梦梦睡衣",
            "price": 66666
        },
        {
            "id": 6,
            "name": "追梦赤子心",
            "price": 555
        }
    ]
    
  • 查询集在models.Book中,即对应的是Book表或模型。
  • search_fields指定了搜索字段为name
  • DRF会自动解析请求中的搜索参数search的值,即
  • 在查询集中,会对name字段进行匹配,找出包含的资源对象。

第三方过滤类

# (2) 过滤:必须继承 GenericAPIView 及其子类,才能使用(如果继承APIView,则不能这么写)
# 第三方过滤类: pip3.9 install django-filter
from django_filters.rest_framework import DjangoFilterBackend


class BookView(GenericViewSet, ListModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [DjangoFilterBackend]  # 排序类
    # SearchFilter:必须配合一个类属性
    # 按照那些字段进行筛选
    # http://127.0.0.1:8000/app01/v1/books/?search=梦
    # 根据指定字段进行筛选,按名字和价格精准匹配
    filterset_fields = ['name', 'price']
  • from django_filters.rest_framework import DjangoFilterBackend
    • 导入使用的第三方过滤器类DjangoFilterBackend
    • 该类是基于Django框架的django-filter库提供的过滤器。
  • GenericViewSetListModelMixin
    • 同样是DRF提供的视图集和列表模型混合类,用于创建支持列表操作的视图。
  • queryset = models.Book.objects.all()
    • 定义查询集,即需要进行过滤的资源对象集合。
  • serializer_class = BookSerializer
    • 定义序列化器类,用于序列化和反序列化数据。
  • filter_backends = [DjangoFilterBackend]
    • 指定过滤器后端,将DjangoFilterBackend添加到过滤器列表中。
  • filterset_fields = ['name', 'price']
    • 通过filterset_fields属性指定过滤字段,在示例中为nameprice
  • {{host}}app01/v1/books/?name=追梦赤子心

    [
        {
            "id": 6,
            "name": "追梦赤子心",
            "price": 555
        }
    ]
    
  • 查询集在models.Book中,即对应的是Book表或模型。
  • filterset_fields指定了过滤字段为nameprice
  • DjangoFilterBackend会自动解析请求中的过滤参数name的值,即追梦赤子心
  • 在查询集中,会根据name字段进行精确匹配过滤,找出名字为追梦赤子心的资源对象。

自定义过滤类

# -*-coding: Utf-8 -*-
# @File : book_filter .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time:2023/7/30
from rest_framework import filters
from django.db.models import Q


class BookFilter(filters.BaseFilterBackend):
    '''
        def filter_queryset(self, request, queryset, view):
        """
        Return a filtered queryset.
        """
        raise NotImplementedError(".filter_queryset() must be overridden.")
    '''

    def filter_queryset(self, request, queryset, view):
        # 返回的数据,都是过滤后的数据
        # http://127.0.0.1:8000/app01/v1/books/?price=99&name=追梦赤子心
        # 需要将筛选条件变成price=99或name=追梦赤子心,而第三方写法中访问地址只能是上面的格式
        price = request.query_params.get('price')
        name = request.query_params.get('name')
        queryset = queryset.filter(Q(name=name) | Q(price=price))

        return queryset
  • 该过滤类继承自BaseFilterBackend

    • 并重写了filter_queryset方法来实现自定义的过滤逻辑。
  • 在视图中

    • 通过将该过滤类添加到filter_backends列表中,使得查询结果可以按照指定的条件进行过滤。
  • 通过GET请求传递的参数可以用request.query_params来获取

    • 例如:price = request.query_params.get('price')
    • ``name = request.query_params.get('name')`。
  • 在这个例子中

    • 使用了Q对象来构建查询条件
    • nameprice参数作为过滤条件进行查询操作
    • 并返回符合条件的结果集。
  • 视图
# (2) 过滤:必须继承 GenericAPIView 及其子类,才能使用(如果继承APIView,则不能这么写)
from django_filters.rest_framework import DjangoFilterBackend


class BookView(GenericViewSet, ListModelMixin):
    queryset = models.Book.objects.all()
    # 自定义过滤类
    serializer_class = BookSerializer
    filter_backends = [BookFilter]  # 排序类
  • {{host}}app01/v1/books/?name=追梦赤子心

    [
        {
            "id": 6,
            "name": "追梦赤子心",
            "price": 555
        }
    ]
    
  • 当请求参数为{{host}}app01/v1/books/?name=追梦赤子心
  • 将返回name字段为"追梦赤子心"的数据。
  • {{host}}app01/v1/books/?price=999

    [
        {
            "id": 4,
            "name": "d",
            "price": 999
        }
    ]
    
  • 当请求参数为{{host}}app01/v1/books/?price=999
  • 将返回price字段为999的数据。
  • {{host}}app01/v1/books/?price=999&name=追梦赤子心

    [
        {
            "id": 4,
            "name": "d",
            "price": 999
        },
        {
            "id": 6,
            "name": "追梦赤子心",
            "price": 555
        }
    ]
    
  • 当请求参数为{{host}}app01/v1/books/?price=999&name=追梦赤子心
  • 将返回同时满足name字段为"追梦赤子心"或price字段为999的数据。

【2】小结

  • 过滤类和排序类可以多个一起使用
  • 当有多个过滤条件时,建议将过滤条件数量较多的放在最前面。
  • 这样可以先对数据进行初步的筛选,过滤掉不符合较多条件的数据,从而减少后续处理的数据量,提高查询性能。

【二】排序

  • restful规范中

    • 请求地址中带过滤条件
  • 排序功能的接口:查询所有

  • 必须继承GenericAPIView及其子类
    • 才能使用排序类
    • DRF提供了有一个排序类
  • 如果继承APIView则不能这么使用
  • 路由
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import SimpleRouter
from app01 import views

router = SimpleRouter()

router.register("books", views.BookView, 'books')
urlpatterns = [
    path('admin/', admin.site.urls),
    path("app01/v1/", include(router.urls))
]
  • 视图
# Create your views here.
# (1)排序:必须继承 GenericAPIView 及其子类,才能使用(如果继承APIView,则不能这么写)
# DRF 给我们提供了一个排序类
from rest_framework.filters import OrderingFilter


class BookView(GenericViewSet, ListModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer
    filter_backends = [OrderingFilter]  # 排序类
    # OrderingFilter:必须配合一个类属性
    # 按照那些字段进行排序 
    # 先按价格倒序排,价格一样,再按id倒序排
    # http://127.0.0.1:8000/api/v1/books/?ordering=-price,-id
    ordering_fields = ['price', 'id']
  • {{host}}app01/v1/books/
[
    {
        "id": 1,
        "name": "a",
        "price": 44
    },
    {
        "id": 2,
        "name": "b",
        "price": 666
    },
    {
        "id": 3,
        "name": "c",
        "price": 88
    },
    {
        "id": 4,
        "name": "d",
        "price": 999
    }
]
  • {{host}}app01/v1/books/?ordering=price
[
    {
        "id": 1,
        "name": "a",
        "price": 44
    },
    {
        "id": 3,
        "name": "c",
        "price": 88
    },
    {
        "id": 2,
        "name": "b",
        "price": 666
    },
    {
        "id": 4,
        "name": "d",
        "price": 999
    }
]
  • {{host}}app01/v1/books/?ordering=price,-id
[
    {
        "id": 1,
        "name": "a",
        "price": 44
    },
    {
        "id": 3,
        "name": "c",
        "price": 88
    },
    {
        "id": 2,
        "name": "b",
        "price": 666
    },
    {
        "id": 4,
        "name": "d",
        "price": 999
    }
]
  • {{host}}app01/v1/books/?ordering=-price,id
[
    {
        "id": 4,
        "name": "d",
        "price": 999
    },
    {
        "id": 2,
        "name": "b",
        "price": 666
    },
    {
        "id": 3,
        "name": "c",
        "price": 88
    },
    {
        "id": 1,
        "name": "a",
        "price": 44
    }
]

【三】分页

  • 查询所有接口,过滤和排序了,但是实际上,这个接口,都需要有分页功能

  • 修改接口:

    • 首先,需要对接口进行修改以支持分页功能。

    • 在接口中添加两个参数:pagepage_size

      • page表示当前页码,

      • page_size表示每页显示的数据数量。

    • 这样你就可以通过这两个参数来分页查询数据。

  • 分页展现形式:

    • 根据不同的展现形式,在Web和App/小程序上采取不同的分页交互方式。
      • Web展现形式:

        • 在Web页面上,通常会使用下一页点击的方式实现分页。

        • 当用户点击下一页按钮时,前端会发送包含page参数的请求,后端根据接收到的page参数返回相应页码对应的数据。

      • App/小程序展现形式:

        • 在App或小程序中,常见的分页交互方式是下滑加载下一页。
        • 当用户滑动到页面底部时,前端会发送请求获取下一页数据。
        • 请求中同样包含page参数,后端根据接收到的page参数返回相应页码对应的数据。
  • 无论是Web还是App/小程序,接口都应该按照相同的规则进行分页处理,即根据pagepage_size参数进行数据的查询和返回。

  • drf提供给咱们,三种分页方式

    • 基本分页
      • 基本分页是最常见和最简单的分页方式之一。
      • 它使用两个参数来确定每页显示的数据数量和当前所处的页码。
      • 通常使用的参数是page和`page_size``
        • ``page`表示当前页码
        • page_size表示每页显示的数据数量。
      • 通过在接口请求中传递这两个参数,可以获取相应页码对应的数据。
      • 基本分页适用于小数据量的情况。
    • 偏移分页
      • 偏移分页使用两个参数来确定每页显示的数据数量和从哪个数据位置开始显示。
      • 与基本分页不同的是,偏移分页的参数为page和`page_size``
        • ``page`表示当前页码
        • page_size表示每页显示的数据数量。
      • 通过计算起始位置,可以从数据库中查询数据并返回给用户。
      • 偏移分页适用于固定顺序的数据集,但在数据量较大时可能效率较低。
    • 游标分页
      • 游标分页是一种以数据的特定标识作为游标,根据游标的位置来确定每页显示的数据。
      • 每次接收到请求时,服务端会返回当前页的数据以及下一页的游标,客户端可使用该游标来获取下一页的数据。
      • 游标通常是一个与数据相关的唯一标识,如数据库的主键或创建时间等。
      • 游标分页适用于大型数据库和需要实现快速、高效分页的场景。

【1】基本分页

  • 自定义分页类
# (1)PageNumberPagination: 基本分页
from rest_framework.pagination import PageNumberPagination


class BookNumberPagination(PageNumberPagination):
    # 重写 4 个 类属性
    page_size = 2  # 每页显示的条数

    page_query_param = 'page'  # 路径后面的 参数:page=4(第4页)

    page_size_query_param = 'page_size'  # page=4&page_size=5:查询第4页,每页显示5条

    max_page_size = 5  # 每页最多显示5条

  • 自定义了一个名为BookNumberPagination的分页类
    • 继承自PageNumberPagination
  • 类属性说明:
    • page_size:每页显示的条数,默认值为2条。
    • page_query_param:路径后面指定的参数名,默认为page
      • 例如,http://127.0.0.1:8000/app01/v1/books/?page=4表示查询第4页的数据。
    • page_size_query_param:路径后面指定的参数名,表示每页显示的条数,默认为page_size
      • 例如,http://127.0.0.1:8000/app01/v1/books/?page=4&page_size=5表示查询第4页的数据,每页显示5条。
    • max_page_size:每页最多显示的条数,默认值为5条。
  • 返回结果说明:
    • count:符合查询条件的总记录数,即所有记录的数量。
    • next:下一页的URL链接,如果有下一页数据,则返回对应的URL;否则返回null。
    • previous:上一页的URL链接,如果有上一页数据,则返回对应的URL;否则返回null。
    • results:当前页的数据列表。
  • {{host}}app01/v1/books/

    {
        "count": 6,
        "next": "http://127.0.0.1:8000/app01/v1/books/?page=3",
        "previous": "http://127.0.0.1:8000/app01/v1/books/",
        "results": [
            {
                "id": 3,
                "name": "c",
                "price": 88
            },
            {
                "id": 4,
                "name": "d",
                "price": 999
            }
        ]
    }
    
  • 根据示例返回结果可以看出,每次请求的结果中包含了分页相关的信息
  • 通过nextprevious可以实现在不同页之间进行跳转
  • results中则是当前页的具体数据列表。

【2】偏移分页

  • 自定义分页类
# (2)LimitOffsetPagination:偏移分页
class BookLimitOffsetPagination(LimitOffsetPagination):
    # 重写 4 个 类属性
    default_limit = 2  # 每页显示的条数
    limit_query_param = 'limit'  # limit:3 本页取三条
    offset_query_param = 'offset'  # 偏移量是多少 offset=3&limit:3 : 从第3条开始取3条数据
    max_limit = 5  # 限制每次取的最大条数
  • 自定义分页类:

    • 代码中定义了一个自定义的分页类BookLimitOffsetPagination
    • 它继承自LimitOffsetPagination
  • 在这个类中我们可以重写四个类属性来设置分页的相关参数:

    • default_limit:每页显示的条数,默认值为2。

    • limit_query_param:用于指定每页取多少条数据的查询参数,默认为limit

    • offset_query_param:用于指定偏移量的查询参数,默认为offset

      • 通过设置这个参数,可以使得分页结果实现偏移取值
      • 即从第几条数据开始取,然后取多少条数据。
    • max_limit:限制每次获取的最大条数,默认值为5。

  • 视图
# (3) 分页:必须继承 GenericAPIView 及其子类,才能使用(如果继承APIView,则不能这么写)
# # 第三方过滤类: pip3.9 install django-filter
from app01.paginations.book_pagination import BookLimitOffsetPagination


class BookView(GenericViewSet, ListModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer
    # 配置分页器
    # 只能按一种方式分页,不能放到列表中
    pagination_class = BookLimitOffsetPagination
  • 代码中展示了一个视图类BookView

    • 它继承自GenericViewSetListModelMixin
    • 其中,GenericViewSet是DRF框架中视图类的基类,而ListModelMixin是列表视图的一个辅助类,提供了获取列表数据的方法。
  • 在这个视图类中,配置了以下几个属性:

    • queryset:设置了查询的数据集合,这里使用models.Book.objects.all()获取所有的Book对象。

    • serializer_class:指定了用于序列化数据的序列化器类BookSerializer

    • pagination_class:配置了分页器,指定为自定义的偏移分页类BookLimitOffsetPagination

  • {{host}}app01/v1/books/

    {
        "count": 6,
        "next": "http://127.0.0.1:8000/app01/v1/books/?limit=2&offset=2",
        "previous": null,
        "results": [
            {
                "id": 1,
                "name": "a",
                "price": 44
            },
            {
                "id": 2,
                "name": "b",
                "price": 666
            }
        ]
    }
    
  • 访问URL:通过访问{{host}}app01/v1/books/可以获取分页数据。
  • 结果解析:返回的JSON结果中包含以下信息:
    • count:总计条数,即所有符合查询条件的数据总数。
    • next:下一页的URL链接,如果有下一页,则返回下一页的URL,否则为null。
    • previous:上一页的URL链接,如果有上一页,则返回上一页的URL,否则为null。
    • results:当前页的数据列表,每个元素包含了书籍的ID、名称和价格等信息。
  • http://127.0.0.1:8000/app01/v1/books/?limit=2&offset=2
{
    "count": 6,
    "next": "http://127.0.0.1:8000/app01/v1/books/?limit=2&offset=4",
    "previous": "http://127.0.0.1:8000/app01/v1/books/?limit=2",
    "results": [
        {
            "id": 3,
            "name": "c",
            "price": 88
        },
        {
            "id": 4,
            "name": "d",
            "price": 999
        }
    ]
}
  • 结果解析:返回的JSON结果中包含以下信息:
    • count:总计条数,即所有符合查询条件的数据总数。
    • next:下一页的URL链接,如果有下一页,则返回下一页的URL,否则为null。
    • previous:上一页的URL链接,如果有上一页,则返回上一页的URL,否则为null。
    • results:当前页的数据列表,每个元素包含了书籍的ID、名称和价格等信息。
  • 综上所述,偏移分页是一种常见的分页方式,它通过指定偏移量和每页显示的条数来获取数据,可以通过调整偏移量来实现翻页功能。
  • 在使用偏移分页时,我们可以自定义分页类,通过设置相关属性来控制每页显示的条数、查询参数名称等参数,然后在视图中配置分页器,即可实现按照指定偏移量和每页显示的条数进行分页查询。

【3】游标分页

  • 自定义分页类
# (3)CursorPagination:游标分页
# 只能上一页或下一页,但是速度特别快,经常用于APP上
class BookCursorPagination(CursorPagination):
    # 重写3个类属性
    cursor_query_param = 'cursor'  # 查询参数
    page_size = 2  # 每页显示2条
    ordering = 'id'  # 必须是要分页的数据表中的字段,一般是id
  • 定义了一个自定义的分页类BookCursorPagination,它继承自Django Rest Framework提供的CursorPagination类。
  • 该分页类通过设置一些属性来控制分页的行为,其中包括:
    • cursor_query_param: 指定查询参数名,这里设置为cursor,表示通过该参数来指定游标位置。
    • page_size: 指定每页显示的记录数,这里设置为2条。
    • ordering: 指定按照哪个字段排序进行分页,这里设置为id字段。
  • 视图
# (3) 分页:必须继承 GenericAPIView 及其子类,才能使用(如果继承APIView,则不能这么写)
from app01.paginations.book_pagination import BookCursorPagination


class BookView(GenericViewSet, ListModelMixin):
    queryset = models.Book.objects.all()
    serializer_class = BookSerializer
    # 配置分页器
    # 只能按一种方式分页,不能放到列表中
    pagination_class = BookCursorPagination
  • 视图BookView继承了GenericViewSetListModelMixin,并配置了分页器BookCursorPagination
  • 通过这种配置,视图中返回的数据会进行游标分页处理。
  • {{host}}app01/v1/books/

    {
        "next": "http://127.0.0.1:8000/app01/v1/books/?cursor=cD0y",
        "previous": null,
        "results": [
            {
                "id": 1,
                "name": "a",
                "price": 44
            },
            {
                "id": 2,
                "name": "b",
                "price": 666
            }
        ]
    }
    

返回结果中,可以看到以下信息:

  • "next"表示下一页的URL
    • 例如"next": "http://127.0.0.1:8000/app01/v1/books/?cursor=cD0y"
    • 通过在URL中添加cursor参数,就可以获取下一页的数据。
  • 如果没有下一页数据,则"next"null
  • "previous"表示上一页的URL,与"next"类似。
  • "results"包含当前页的数据列表,其中包括每个书籍的idnameprice
  • "next": "http://127.0.0.1:8000/app01/v1/books/?cursor=cD0y"
    • 无法知道下一页的精确地址
  • 需要注意的是,游标分页无法精确知道下一页的地址,只能通过提供当前页的游标值来获取下一页数据。
  • 由于游标是基于排序字段的位置进行计算的,因此无法提供精确的下一页地址。
  • 这种分页方式适用于移动端等需要快速加载数据的场景。

【4】分页源码分析

  • ListModelMixin ----> list 方法
class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)
  • 分页查询的Mixin类 ListModelMixin,它包含了list()方法用于获取queryset中的数据并进行分页处理。
  • list()方法中
    • 首先通过调用filter_queryset()方法对queryset进行过滤
    • 然后调用paginate_queryset()方法对过滤后的结果进行分页处理。
  • 分页逻辑
if page is not None:
    # 把page进行序列化
    serializer = self.get_serializer(page, many=True)
    return self.get_paginated_response(serializer.data)
  • 如果得到的分页结果不为空(即page is not None
    • 则使用get_serializer()方法将分页后的数据进行序列化
  • 最后通过调用get_paginated_response()方法返回序列化后的数据
  • 返回的数据GenericAPIView ---> get_paginated_response(self, data)
def get_paginated_response(self, data):
    """
        Return a paginated style `Response` object for the given output data.
        """
    assert self.paginator is not None # 类实例化得到对象
    return self.paginator.get_paginated_response(data)

get_paginated_response()方法中,首先确保self.paginator不为空(即存在分页器对象),然后根据指定的输出数据构建一个带有分页信息的Response对象,其中包括当前页面的数据、总数、下一页链接以及上一页链接。

  • self.paginator.get_paginated_response(data)
# get_paginated_response - - 可以指定返回的数据
def get_paginated_response(self, data):
    return Response(OrderedDict([
        ('count', self.count),
        ('next', self.get_next_link()),
        ('previous', self.get_previous_link()),
        ('results', data)
    ]))
  • 上述代码中的 self.paginator.get_paginated_response(data) 是在 ListModelMixin 类中用于返回分页数据的逻辑。

  • 具体来说

    • get_paginated_response(data) 方法接收一个参数 data,该参数是经过序列化后的分页数据。

    • 在这个方法中,使用 OrderedDict 构建了一个字典对象,该字典包含了以下键值对:

      • 'count'

        • 代表总数的键,用于存储整个查询结果的总数。
      • 'next'

        • 代表下一页链接的键,通过调用 get_next_link() 方法获取下一页的链接。
        • 如果不存在下一页,则返回 None
      • 'previous'

        • 代表上一页链接的键,通过调用 get_previous_link() 方法获取上一页的链接。
        • 如果不存在上一页,则返回 None
      • 'results'

        • 代表当前页面数据的键,存储了经过序列化后的分页数据。
  • 最后,将构建的字典作为参数传递给 Response 构造函数,并返回生成的 Response 对象。

  • 因此,self.paginator.get_paginated_response(data) 方法的作用是将分页数据以及相关的分页信息组装成一个字典,然后用 Response 对象进行封装并返回给调用者。

自定义分页 ---- 返回全部数据

class BookView(APIView):
    back_dict = {"code": 1000, "msg": "", "result": []}

    def get(self, request):

        # 排序条件
        order_param = request.query_params.get('ordering')
        # 过滤条件
        filter_name = request.query_params.get('name')
        book_obj = Book.objects.all()
        if order_param:
            book_obj = book_obj.order_by(order_param)
        if filter_name:
            # 包含过滤条件的被过滤出来
            book_obj = book_obj.filter(name__contains=filter_name)

        # 分页
        pagination = BookLimitOffsetPagination()
        page = pagination.paginate_queryset(book_obj, request, self)

        # 序列化
        book_ser = BookSerializer(instance=page, many=True)

        self.back_dict["msg"] = "请求数据成功"
        # self.back_dict["result"] = pagination.get_paginated_response(book_ser.data)

        return pagination.get_paginated_response(book_ser.data)
  • 上述代码实现了一个自定义分页功能,通过 BookLimitOffsetPagination 类对 book_obj 进行分页处理,并返回分页结果。
  • get 方法中,首先获取请求参数中的排序条件 order_param 和过滤条件 filter_name。然后通过 Book.objects.all() 获取所有的 Book 对象。
  • 接下来,根据排序条件和过滤条件对 book_obj 进行排序和过滤操作,得到符合条件的查询结果。
  • 然后,创建 BookLimitOffsetPagination 类的实例 pagination。通过调用 pagination.paginate_queryset(book_obj, request, self) 方法对查询结果进行分页处理,其中 request 是当前请求对象,self 是当前视图对象。
  • 接着,使用序列化器 BookSerializer 对分页后的结果 page 进行序列化,得到序列化后的数据 book_ser
  • 之后,更新 self.back_dict"msg" 键的值为 "请求数据成功"。
  • 最后,通过调用 pagination.get_paginated_response(book_ser.data) 方法,将序列化后的分页数据传入,该方法会返回包含分页信息的字典对象作为响应结果。
  • 综上所述,当访问 {{host}}app01/v1/books/ 时,会返回一个带有分页信息的响应结果,其中 "count" 表示总数,"next" 表示下一页链接,"previous" 表示上一页链接,"results" 表示当前页数据。
  • {{host}}app01/v1/books/

    {
        "count": 6,
        "next": "http://127.0.0.1:8000/app01/v1/books/?limit=2&offset=2",
        "previous": null,
        "results": [
            {
                "id": 1,
                "name": "a",
                "price": 44
            },
            {
                "id": 2,
                "name": "b",
                "price": 666
            }
        ]
    }
    

    自定义分页 ---- 返回自定义数据

class BookView(APIView):
    back_dict = {"code": 1000, "msg": "", "result": []}

    def get(self, request):

        # 排序条件
        order_param = request.query_params.get('ordering')
        # 过滤条件
        filter_name = request.query_params.get('name')
        book_obj = Book.objects.all()
        if order_param:
            book_obj = book_obj.order_by(order_param)
        if filter_name:
            # 包含过滤条件的被过滤出来
            book_obj = book_obj.filter(name__contains=filter_name)

        # 分页
        pagination = BookLimitOffsetPagination()
        page = pagination.paginate_queryset(book_obj, request, self)

        # 序列化
        book_ser = BookSerializer(instance=page, many=True)

        self.back_dict["msg"] = "请求数据成功"
        '''
        # get_paginated_response - - 可以指定返回的数据
		def get_paginated_response(self, data):
    		return Response(OrderedDict([
                ('count', self.count),
                ('next', self.get_next_link()),
                ('previous', self.get_previous_link()),
                ('results', data)
   			 ]))
        '''
        self.back_dict['count'] = pagination.count
        self.back_dict['next'] = pagination.get_next_link()

        return Response(self.back_dict)
  • {{host}}app01/v1/books/

    {
        "code": 1000,
        "msg": "请求数据成功",
        "result": [],
        "count": 6,
        "next": "http://127.0.0.1:8000/app01/v1/books/?limit=2&offset=2"
    }
    

【四】基于APIView写排序分页

【1】排序过滤

  • 路由
path("app01/v1/books/", views.BookView.as_view())
  • 视图
class BookView(APIView):
    back_dict = {"code": 1000, "msg": "", "result": []}

    def get(self, request):

        # 排序条件
        order_param = request.query_params.get('ordering')
        # 过滤条件
        filter_name = request.query_params.get('name')
        book_obj = Book.objects.all()
        if order_param:
            book_obj = book_obj.order_by(order_param)
        if filter_name:
            # 包含过滤条件的被过滤出来
            book_obj = book_obj.filter(name__contains=filter_name)

        # 序列化
        book_ser = BookSerializer(instance=book_obj, many=True)

        self.back_dict["msg"] = "请求数据成功"
        self.back_dict["result"] = book_ser.data

        return Response(self.back_dict)
  • 序列化类
from app01 import models
from rest_framework import serializers


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = "__all__"
  • {{host}}app01/v1/books/

    {
        "code": 1000,
        "msg": "请求数据成功",
        "result": [
            {
                "id": 1,
                "name": "a",
                "price": 44
            },
            {
                "id": 2,
                "name": "b",
                "price": 666
            },
            {
                "id": 3,
                "name": "c",
                "price": 88
            },
            {
                "id": 4,
                "name": "d",
                "price": 999
            },
            {
                "id": 5,
                "name": "小梦梦睡衣",
                "price": 66666
            },
            {
                "id": 6,
                "name": "追梦赤子心",
                "price": 555
            }
        ]
    }
    
  • {{host}}app01/v1/books/?ordering=-price

    {
        "code": 1000,
        "msg": "请求数据成功",
        "result": [
            {
                "id": 5,
                "name": "小梦梦睡衣",
                "price": 66666
            },
            {
                "id": 4,
                "name": "d",
                "price": 999
            },
            {
                "id": 2,
                "name": "b",
                "price": 666
            },
            {
                "id": 6,
                "name": "追梦赤子心",
                "price": 555
            },
            {
                "id": 3,
                "name": "c",
                "price": 88
            },
            {
                "id": 1,
                "name": "a",
                "price": 44
            }
        ]
    }
    
  • {{host}}app01/v1/books/?ordering=-price&name=梦

    {
        "code": 1000,
        "msg": "请求数据成功",
        "result": [
            {
                "id": 5,
                "name": "小梦梦睡衣",
                "price": 66666
            },
            {
                "id": 6,
                "name": "追梦赤子心",
                "price": 555
            }
        ]
    }
    

【2】排序过滤和分页

  • 视图

posted @ 2023-07-31 21:46  Chimengmeng  阅读(75)  评论(0编辑  收藏  举报