天天看點

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.')
      

  

這裡我們重點介紹一下類的屬性,重點的參數如下,等下我們會測試的

Django的rest_framework的分頁元件源碼分析

 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這個方法的參數,和源碼中的參數對應一下

Django的rest_framework的分頁元件源碼分析

 源碼中的參數

Django的rest_framework的分頁元件源碼分析

 6、下面我們測試一下

每頁顯示一條資料,顯示第一頁

Django的rest_framework的分頁元件源碼分析

每頁顯示1條資料,顯示第二頁

Django的rest_framework的分頁元件源碼分析

類中設定每頁顯示1條資料,我們臨時修改為顯示2條資料,顯示第一頁

Django的rest_framework的分頁元件源碼分析

 類中設定每頁顯示1條資料,我們臨時修改為顯示5條資料,顯示第一頁,理論上5是不生效的,實際隻顯示了4條資料

Django的rest_framework的分頁元件源碼分析

經過測試,我們上面的四個參數都已經生效 

最後在補充一點,上面的每頁顯示多少條的設定針對單個表生效,如何基于所有的表生效。

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'
      

  

重點看下下面幾個屬性

Django的rest_framework的分頁元件源碼分析

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)
      

  

重點是這裡

Django的rest_framework的分頁元件源碼分析

 5、最後我們測試一下

預設不傳參數,每頁顯示2條

Django的rest_framework的分頁元件源碼分析

臨時修改每頁顯示為1個,偏移量為1

Django的rest_framework的分頁元件源碼分析

 臨時修改每頁顯示1條,偏移量為2

Django的rest_framework的分頁元件源碼分析

三、基于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個父類

Django的rest_framework的分頁元件源碼分析

 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這個方法,首先要先找到這個方法

Django的rest_framework的分頁元件源碼分析

 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這個屬性就可以了

Django的rest_framework的分頁元件源碼分析

 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的分頁元件源碼分析

 最後我們測試一下

下面這個是我們禁用分頁的顯示效果

Django的rest_framework的分頁元件源碼分析

 下面開始啟用分頁的效果,顯示第一頁

Django的rest_framework的分頁元件源碼分析

 顯示第二頁的效果

Django的rest_framework的分頁元件源碼分析

總結:至此,Django的Rest_framework的分頁元件就介紹完了,大家有不清楚的,可以留言,我們共同進步。