• Django的rest_framework的分页组件源码分析


    前言:

      分页大家应该都很清楚,今天我来给大家做一下Django的rest_framework的分页组件的分析;我的讲解的思路是这样的,分别使用APIview的视图类和基于ModelViewSet的视图类两种方式实现分页的功能,同时我也会介绍两个分页的类,PageNumberPagination类和LimitOffsetPagination,希望能对大家有所帮助!

      今天的博客主要的这样的,先讲解基于APIView类的两种分页类的实现方式,然后在讲解基于ModelViewSet类的两种分页类的实现方式

    正文:

    一、基于APIView的视图类,使用PageNumberPagination

    1、先导入我们要用到的分页的类

    from rest_framework.pagination import PageNumberPagination
    

      

    2、下面我们来看下这个这个类的类是属性

    class PageNumberPagination(BasePagination):
        """
        A simple page number based style that supports page numbers as
        query parameters. For example:
    
        http://api.example.org/accounts/?page=4
        http://api.example.org/accounts/?page=4&page_size=100
        """
        # The default page size.
        # Defaults to `None`, meaning pagination is disabled.
        page_size = api_settings.PAGE_SIZE
    
        django_paginator_class = DjangoPaginator
    
        # Client can control the page using this query parameter.
        page_query_param = 'page'
        page_query_description = _('A page number within the paginated result set.')
    
        # Client can control the page size using this query parameter.
        # Default is 'None'. Set to eg 'page_size' to enable usage.
        page_size_query_param = None
        page_size_query_description = _('Number of results to return per page.')
    
        # Set to an integer to limit the maximum page size the client may request.
        # Only relevant if 'page_size_query_param' has also been set.
        max_page_size = None
    
        last_page_strings = ('last',)
    
        template = 'rest_framework/pagination/numbers.html'
    
        invalid_page_message = _('Invalid page.')
    

      

    这里我们重点介绍一下类的属性,重点的参数如下,等下我们会测试的

     3、然后看下类的paginate_queryset方法,这个方法主要就是用来显示数据的

        def paginate_queryset(self, queryset, request, view=None):
            """
            Paginate a queryset if required, either returning a
            page object, or `None` if pagination is not configured for this view.
            """
            page_size = self.get_page_size(request)
            if not page_size:
                return None
    
            paginator = self.django_paginator_class(queryset, page_size)
            page_number = request.query_params.get(self.page_query_param, 1)
            if page_number in self.last_page_strings:
                page_number = paginator.num_pages
    
            try:
                self.page = paginator.page(page_number)
            except InvalidPage as exc:
                msg = self.invalid_page_message.format(
                    page_number=page_number, message=six.text_type(exc)
                )
                raise NotFound(msg)
    
            if paginator.num_pages > 1 and self.template is not None:
                # The browsable API should display pagination controls.
                self.display_page_controls = True
    
            self.request = request
            return list(self.page)
    

      

    4、PageNumberPagination用到知识点,我们已经讲解完了,下面我们介绍下如何使用,首先我们先继承一下PageNumberPagination,然后自定义我们的参数

    class MyPageNumberPagination(PageNumberPagination):
        page_size = 1
        page_query_param = "mypage"
        page_size_query_param = "size"
        max_page_size = 4
        last_page_strings = ('mylast',)
    

      

    5、然后在视图类中使用我们自己写的分页类

    class Book_cbv(APIView):
        authentication_classes = []
        # permission_classes = [SVIPpermission(),]
        # throttle_classes = [throttlerate(),]
        # parser_classes = []
        def get(self,request):
    
            query_list = models.Book.objects.all()
            mypageobj = MyPageNumberPagination()
            obj = mypageobj.paginate_queryset(queryset=query_list,request=request,view=None)
            bs = bookmodelserializer(obj,many=True,context={'request': request})
    

      

    重点看下这里,关注一下paginate_queryset这个方法的参数,和源码中的参数对应一下

     源码中的参数

     6、下面我们测试一下

    每页显示一条数据,显示第一页

     

    每页显示1条数据,显示第二页

    类中设定每页显示1条数据,我们临时修改为显示2条数据,显示第一页

     类中设定每页显示1条数据,我们临时修改为显示5条数据,显示第一页,理论上5是不生效的,实际只显示了4条数据

    经过测试,我们上面的四个参数都已经生效 

    最后在补充一点,上面的每页显示多少条的设置针对单个表生效,如何基于所有的表生效。

    class PageNumberPagination(BasePagination):
        """
        A simple page number based style that supports page numbers as
        query parameters. For example:
    
        http://api.example.org/accounts/?page=4
        http://api.example.org/accounts/?page=4&page_size=100
        """
        # The default page size.
        # Defaults to `None`, meaning pagination is disabled.
        page_size = api_settings.PAGE_SIZE
    

      

    然后看下api_setttings水里对象

    api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
    

      

    看下APISettigns这个类

        def __init__(self, user_settings=None, defaults=None, import_strings=None):
            if user_settings:
                self._user_settings = self.__check_user_settings(user_settings)
            self.defaults = defaults or DEFAULTS
            self.import_strings = import_strings or IMPORT_STRINGS
            self._cached_attrs = set()
    

      

    到了这里,我们大致就知道了,我们需要进入我们的project的settings文件中进行设置

    REST_FRAMEWORK = {
    #     "DEFAULT_AUTHENTICATION_CLASSES":(
    #         "app1.utils.Book_auther",
    #     ),
    #     "DEFAULT_PERMISSION_CLASSES_CLASSES": (
    #         "app1.utils.SVIPpermission",
    #     ),
    #     "DEFAULT_DEFAULT_THROTTLE_CLASSES_CLASSES": (
    #         "app1.utils.throttlerate",
    #     )
        "PAGE_SIZE" : 2,
    }
    

      

    二、基于APIView的视图类,使用LimitOffsetPagination类

    1、导入LimitOffsetPagination类

    from rest_framework.pagination import LimitOffsetPagination
    

      

    2、我们同样看下这个类的属性

    class LimitOffsetPagination(BasePagination):
        """
        A limit/offset based style. For example:
    
        http://api.example.org/accounts/?limit=100
        http://api.example.org/accounts/?offset=400&limit=100
        """
        default_limit = api_settings.PAGE_SIZE
        limit_query_param = 'limit'
        limit_query_description = _('Number of results to return per page.')
        offset_query_param = 'offset'
        offset_query_description = _('The initial index from which to return the results.')
        max_limit = None
        template = 'rest_framework/pagination/numbers.html'
    

      

    重点看下下面几个属性

    3、然后看下LimitOffsetPagination类的paginate_queryset的方法

        def paginate_queryset(self, queryset, request, view=None):
            self.count = self.get_count(queryset)
            self.limit = self.get_limit(request)
            if self.limit is None:
                return None
    
            self.offset = self.get_offset(request)
            self.request = request
            if self.count > self.limit and self.template is not None:
                self.display_page_controls = True
    
            if self.count == 0 or self.offset > self.count:
                return []
            return list(queryset[self.offset:self.offset + self.limit])
    

      

    4、然后我们看下如何在我们的视图类中使用这个分页插件,其实和前面的插件的用法是一样的,只是参数不一样而已

    class Book_cbv(APIView):
        authentication_classes = []
        # permission_classes = [SVIPpermission(),]
        # throttle_classes = [throttlerate(),]
        # parser_classes = []
        def get(self,request):
    
            query_list = models.Book.objects.all()
            # mypageobj = MyPageNumberPagination()        
            # obj = mypageobj.paginate_queryset(queryset=query_list,request=request,view=None)
            mypageobj = MyLimitOffsetPagination()
            obj = mypageobj.paginate_queryset(queryset=query_list,request=request,view=None)
            bs = bookmodelserializer(obj,many=True,context={'request': request})
    
    
            return Response(bs.data)
    

      

    重点是这里

     5、最后我们测试一下

    默认不传参数,每页显示2条

    临时修改每页显示为1个,偏移量为1

     临时修改每页显示1条,偏移量为2

    三、基于ModelViewSet的视图类,实现分页功能

    通过上面的讲解,我们知道两个分页的类就是参数不一样,使用的逻辑都一样,我们这里就不分开讲解2个类,只讲一下如何ModelViewSet类实现分页

    1、先看下基于ModelViewSet的视图类

    from rest_framework import viewsets
    
    class AutherModelCBV(viewsets.ModelViewSet):
        queryset = models.Auther.objects.all()
        serializer_class = authermodelserializer
    

      

    2、因为这个ModelViewSet这个类重写了list方法,分页肯定在list方法中,所以我们先要找到list方法,ModelViewSet这个类一共有4个父类

     3、我们看下mixins.ListModelMixin这个类,因为list方法是在这个类中实现的,进入这个类,看下list方法

    class ListModelMixin(object):
        """
        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)
    

      

    4、重点看下paginate_queryset这个方法,首先要先找到这个方法

     5、如果找这个方法呢,我们从最开始找这个方法

    首先AutherModelCBV这个类没有这个方法

    from rest_framework import viewsets
    
    class AutherModelCBV(viewsets.ModelViewSet):
        queryset = models.Auther.objects.all()
        serializer_class = authermodelserializer
    

    然后去ModelViewSet类中找这个方法,同样没有

    class ModelViewSet(mixins.CreateModelMixin,
                       mixins.RetrieveModelMixin,
                       mixins.UpdateModelMixin,
                       mixins.DestroyModelMixin,
                       mixins.ListModelMixin,
                       GenericViewSet):
        """
        A viewset that provides default `create()`, `retrieve()`, `update()`,
        `partial_update()`, `destroy()` and `list()` actions.
        """
        pass
    

      

    ModelViewSet这个类继承了有5个类,我们从左到右一个一个看,最终在GenericViewSet,我们看到这个类还继承了2个类

    class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
        """
        The GenericViewSet class does not provide any actions by default,
        but does include the base set of generic view behavior, such as
        the `get_object` and `get_queryset` methods.
        """
        pass
    

      

    再次从左到右依次查找paginate_queryset这个方法,最终下面这个类中找到这个方法

    class GenericAPIView(views.APIView):
    

      

    方法的源码如下

        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)
    

      

    那么这个paginator是什么呢,原来他是静态方法

        @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
    

      

    到这里,我们就非常清楚的了,我们需要在我们自己的视图类中定义一个这样的属性,然后把我们的分页类赋值给pagination_class这个属性就可以了

     6、然后看下在ModelViewSet类中如何操作

    from rest_framework import viewsets
    
    class AutherModelCBV(viewsets.ModelViewSet):
        queryset = models.Auther.objects.all()
        serializer_class = authermodelserializer
        pagination_class = MyPageNumberPagination
    

      

    重点看下这里

     最后我们测试一下

    下面这个是我们禁用分页的显示效果

     下面开始启用分页的效果,显示第一页

     显示第二页的效果

    总结:至此,Django的Rest_framework的分页组件就介绍完了,大家有不清楚的,可以留言,我们共同进步。 

  • 相关阅读:
    测试杂谈
    使用jQuery完成表单验证
    session&&cookie
    jQuery中关于toggle的使用
    Regist&Login
    关于线程的面试题
    成语验证码所需素材
    验证码测试-demo
    java动态生成验证码图片
    servlet-向页面输出中文出现乱码处理方式
  • 原文地址:https://www.cnblogs.com/bainianminguo/p/10508191.html
Copyright © 2020-2023  润新知