首页 > 其他分享 >Django restframwork中使用分页及实现自定义分页

Django restframwork中使用分页及实现自定义分页

时间:2023-07-06 16:32:55浏览次数:65  
标签:restframwork 分页 自定义 self queryset page class

关于为何要分页 以及如何在Django+Template架构中如何使用分页,可以参考之前的文章

django 自定义分页类和使用总结[1]


Django RestFramework中分页限制

今天开篇我们先不讲如何使用,我们先说 Django + restframework 实现前后端分离项目开发时, 分页功能使用的限制?

缘由是之前在开发运维平台的时候,没有正确使用分页功能,导致 自定义的分页在不同情况下,有的不能用,有的能用。最终原因是看实际使用的时候是 继承的那个类

这里根据实例使用类视图 时的继承关系梳理了如下图

Django restframwork中使用分页及实现自定义分页_django

Django RestFramework 列表相关类视图继承关系

这里需要重点关注的是:

1、generics.ListAPIViewgenerics.ListCreateAPIView 继承自对应的 XXXModelMixin 和 GenericAPIView

2、ModelViewSet 继承自 对应的 XXXModelMixin 和 GenericViewSet, 而 GenericViewSet又继承自 ViewSetMixin 和 GenericAPIView

我们查阅 RestFramework 的源码知道,关于分页的逻辑实现,是在 GenericAPIView中实现的,

class GenericAPIView(views.APIView):
    ... ...

    # The style to use for queryset pagination.
    pagination_class = api_settings.DEFAULT_PAGINATION_CLASS

    ... ...

    @property
    def paginator(self):
        """
        The paginator instance associated with the view, or `None`.
        """
        if not hasattr(self, '_paginator'):
            if self.pagination_class is None:
                self._paginator = None
            else:
                self._paginator = self.pagination_class()
        return self._paginator

    def paginate_queryset(self, queryset):
        """
        Return a single page of results, or `None` if pagination is disabled.
        """
        if self.paginator is None:
            return None
        return self.paginator.paginate_queryset(queryset, self.request, view=self)

    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)

而使用分页的情况下,都会实现list()方法,而在 ListModelMixin中 就调用了 self.paginate_queryset 和 self.get_paginated_response

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)

所以综上所述:

Django Restframework 中只有继承以下类的情况才可以使用分页

  • • viewsets.ModelViewSet
  • • generics.ListAPIView
  • • generics.ListCreateAPIView

继承 ModelViewSet 实现分页

1、先定义 serializer

为了方便,我们直接使用 ModelSerializer

# appdemo.serializers.py

from rest_framework.serializers import ModelSerializer
from appdemo.models import Post


class PostModelSerializer(ModelSerializer):
    class Meta:
        model = Post
        fields = '__all__'

2、定义viewset

from rest_framework.viewsets import ModelViewSet
# 这里导入
from rest_framework.pagination import PageNumberPagination

from appdemo.models import Post
from appdemo.serializers import PostModelSerializer

class PostModelViewSet(ModelViewSet):
    serializer_class = PostModelSerializer
    queryset = Post.objects.all()
    # 这是视图级别配置分页
    pagination_class = PageNumberPagination

使用 RestFramework 分页时,也可以全局配置

# settings.py
REST_FRAMEWORK ={
    # 全局配置
    # 'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',

    # 但是至少要配置 PAGE_SIZE, 因为系统默认 PAGE_SIZE = NONE 相当于没有分页
    'PAGE_SIZE': 3 
}

3、配置viewset对应的路由

和 一般类视图不一样的是, ModelViewSet 定义路由使用如下配置

# appdemo.urls.py
from appdemo.views import PostModelViewSet

router = DefaultRouter()
router.register('post', PostModelViewSet)

urlpatterns = [... ...]
urlpatterns += router.urls

然后就可以通过 http://127.0.0.1:8092/demo/post/ 来访问了

继续 ListAPIView 实现分页

# 这里导入
from rest_framework.pagination import PageNumberPagination
from rest_framework.generics import ListAPIView
from appdemo.models import Post
from appdemo.serializers import PostModelSerializer


class PostListAPIView(ListAPIView):
    serializer_class = PostModelSerializer
    queryset = Post.objects.all()
    pagination_class = PageNumberPagination

和上面 ModelViewSet 不同的是, ListAPIView 只是实现了list()方法,而 ModelViewSet 实现 create()list()retrieve()update() 和 destory() 方法

自定义分页

其实RestFramework 默认提供了三种分页方式

  • • PageNumberPagination 普通分页器

支持用户按?page=3&size=10这种更灵活的方式进行查询,这样用户不仅可以选择页码,还可以选择每页展示数据的数量。

但是一般强烈建议要配置 max_page_size, 防止超大数据量的”恶意“查询

  • • LimitOffsetPagination 偏移分页器

支持用户按?limit=20&offset=100这种方式进行查询。offset是查询数据的起始点,limit是每页展示数据的最大条数,类似于page_size。

偏移分页器建议设置 max_limit 限制单页查询的数据量

  • • CursorPagination 游标分页器

这是DRF提供的加密分页查询, 不返回具体的页码、大小或者是起始位置,仅支持用户按响应提供的上一页和下一页链接进行分页查询,每页的页码都是加密的。

使用这种方式进行分页需要你的模型有”created”这个字段,否则你要手动指定ordering排序才能进行使用。

了解这三种分页器之后,下面我们开始自定义

# appdemo.pagination.py

from rest_framework.pagination import PageNumberPagination


class CwsPageNumberPagination(PageNumberPagination):
    # 覆盖默认的PAGE_SIZE = None
    page_size = 5
    # 自定义页面大小参数
    page_size_query_param = 'size'
    # 设置每页最大数据量, 默认为None,就是不限制
    max_page_size = 100
    # 自定义页码参数
    page_query_param = 'page'

一般如上代码基本就满足我们定制化的需求,但是在前后端分离架构中,有时候会 自定义响应 。

如果存在分页的话,从 mixins.ListModelMixin 类中我们知道它是直接使用 rest_framework.response.Response 进行返回的。 (参考文章上面提到的源码)

那么在ModelViewSet中就存在 list() 是 按照 rest_framework.response.Response 返回,而其他方法按照 自定义响应 返回,就存在返回格式不统一的问题

所以这个时候,一般需要在自定义分页器中实现get_paginated_response()方法,该方法中使用自定义响应类返回就行。

具体如何自定义响应,我们后续文章在介绍

使用自定义分页很简单,在全局配置文件settings.py 中配置 REST_FRAMEWORK 的 DEFAULT_PAGINATION_CLASS 值为 自定义分页器 或者 在视图类中配置 pagination_class 。

具体实现之后的效果, 建议大家自己动手尝试下, 效果如下

Django restframwork中使用分页及实现自定义分页_pagination_02

django restframework 自定义PageNumber分页器

关于 LimitOffsetPagination 偏移分页器 和 CursorPagination 游标分页器

这里直接给出相关代码,感兴趣的可以自己挨个敲代码实现看看效果,更容易加深理解

# appdemo.pagination.py

class CwsLimitOffsetPagination(LimitOffsetPagination):
    # 默认页面大小
    default_limit = 5  
    # 最大页面限制
    max_limit = 10
    # 页面 大小参数和 起始参数
    limit_query_param = 'limit'  
    offset_query_param = 'offset'  


class CwsCursorPagination(CursorPagination):
    # page_size 和 page_size_query_param 含义和 PageNumberPagination 一样
    page_size = 3
    page_size_query_param = 'page_size' 
    # 默认的游标参数
    cursor_query_param = 'cursor'
    # 如果模型中没有 created 字段,那么就需要 明确配置 oridering 
    ordering = '-create_date'

今天的知识就介绍到这里。 如果觉得文章对你有用,请不吝点赞 和 关注个人公众号(搜索 全栈运维 或者 DailyJobOps)

引用链接

[1] django 自定义分页类和使用总结: http://www.colinspace.com/blog/post/53/

标签:restframwork,分页,自定义,self,queryset,page,class
From: https://blog.51cto.com/colinspace/6643467

相关文章

  • vue刷新页面时保持当前分页不变(使用本地存储保存页码)
    this.currentPage=1原本的代码是在页面构造时直接传入第一页的页码使得页面去读取第一页应有的数据。 解决思路:设置一个变量,用于保存每一次刷新前的页面页码数,页面构造函数里进行判断如果这个变量为空,那么说明是第一次加载页面,页面读取第一页数据。在需要刷新前将当前页码保......
  • Vue 先初始化子组件再初始化父组件的方法(自定义父子组件mounted执行顺序)
    写在前面:本篇内容内容主要讲述了,在使用Konva进行开发过程中遇到的一些问题。(既然是组件加载顺序,主要牵扯到的就是,父子组件的关系,父子组件的生命周期)众所周知,Vue中父子组件生命周期的执行顺序为://挂载阶段父beforeCreate->父created->父beforeMount->子beforeCre......
  • 10.分页
    1.el-table:<el-table:data="tableData.slice((currentPage-1)*pageSize,currentPage*pageSize)">···</el-table> 2.el-pagination:<divstyle="text-align:right;"><el-paginationbackground......
  • ERP导出表格自定义格式R报表开发
    按照正常流程新建程序,画面修改上传,程序下载修改导入JAVA包,在global.import下 IMPORTcomIMPORTJAVAjava.net.URLIMPORTJAVAorg.apache.poi.ss.util.CellRangeAddressIMPORTJAVAorg.apache.poi.ss.util.RegionUtilIMPORTJAVAjava.io.InputStreamIMPORTJAVAjava......
  • 前端Vue自定义顶部导航栏navBar 导航栏搜索框searchBar 导航栏右侧菜单按钮button
    前端Vue自定义顶部导航栏navBar导航栏搜索框searchBar导航栏右侧菜单按钮button,下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=13342效果图如下:cc-headerSearch使用方法<!--icon:右侧菜单图标@searchClick:搜索点击 @rigIconClick:右......
  • 前端Vue自定义带历史记录的搜索框组件searchBar 支持搜索输入框清空 搜索历史存储记录
    前端Vue自定义带历史记录的搜索框组件searchBar支持搜索输入框清空搜索历史存储记录清除,下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id=13343效果图如下:cc-hisSearchBar使用方法//不同的业务功能历史记录设置不同存储keyconstkStora......
  • 分页查询-前端
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title><style>.el-table.warning-row{background:oldlace;}.el-......
  • 分页查询-后台
         ......
  • 自定义异常类-MyException
    packageutils;publicclassMyExceptionextendsException{ publicMyException(){ super(); } publicMyException(Strings){ super(s); }} ......
  • 分页查询-分析
        packagecom.itheima.pojo;importjava.util.List;//分页查询JavaBean//<T>为了更好的契合各种各样的查询<Brand>/<User>publicclassPageBean<T>{//总记录数privateinttotalCount;//当前页数据privateList<T>row......