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

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

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


Django RestFramework中分页限制

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

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

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

Django restframwork中使用分页及实现自定义分页_第1张图片

这里需要重点关注的是:

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_querysetself.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中使用分页及实现自定义分页_第2张图片

关于 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)

你可能感兴趣的:(Django,django,python,pagination,分页,restframework)