47.DRF實現分頁

木子七發表於2022-12-29

分頁Pagination

  當我們在PC 或者 App 有大量資料需要展示時,可以對資料進行分頁展示。這時就用到了分頁功能,分頁使得資料更好的展示給使用者
  比如我們有1W+資料要返回給前端,資料量大一次性返回可能會比較慢,前端一次性展示1W+資料也會比較慢,用分頁返回資料效果較好

前端分頁和後端分頁的區別

前端分頁
前端分頁是一次性把資料全部拿出來進行分頁,比如500條資料,一次展示50條,點選下一頁再展示下50條,依次類推
 
優缺點:
前端分頁一次性把資料載入出來,翻頁過程中不會再對後端發起請求,對伺服器壓力較小
但是當資料量過大的時候,比較耗費效能,載入速度會比較慢
 
後端分頁
後端分頁是根據前端的需求返回對應的資料給客戶端,例如一共有500條資料,一次展示50條,前端傳參請求一次只返回50條資料
傳下一頁的page再請求下一頁的資料再返回50條
 
優缺點:
後端分頁比較靈活,每次都單獨請求資料,對前端效能要求不高, 更易保證資料準確性
因為每次翻頁都需要請求後端拿到對應的資料,多使用者同時請求會增加後端壓力
 
如何選擇分頁

資料量比較小的時候建議一次性返回資料在前端進行分頁,資料量龐大的話建議伺服器分頁單次請求單次返回

 

DRF中的分頁

DRF中的分頁介紹
在drf框架中允許自定製分頁樣式,可以設定每頁顯示的數量,並且支援以下操作
  • 將分頁連結作為響應內容的一部分
  • 響應頭中包含分頁連結,比如Content-Range或Link
 
drf中使用通用檢視或者檢視集的時候會自動執行分頁,如果使用的常規的APIView等,需要自己呼叫分頁API
  • 可以透過將分頁類設定None來選擇是否關閉分頁功能
 
自有分頁VIew原始碼示例
我們拿ListAPIView舉例,ListAPIView繼承mixins.ListModelMixin和GenericAPIView,下述程式碼可以看出原始碼本身包含了分頁功能,如果使用常規VIew則需要自己實現對應邏輯
# ListModelMixin原始碼 如果是常規view要實現分頁,在檢視中實現下述程式碼即可
class ListModelMixin:
 
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        # self.paginate_queryset對原有的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)
 
 
分頁的settings設定
DEFAULT_PAGINATION_CLASS和PAGE_SIZE必須同時設定,如果不設定或者只設定一個,就相當於是None不分頁
REST_FRAMEWORK = {
    
    # drf的分頁類位於rest_framework.pagination中
    "DEFAULT_PAGINATION_CLASS":"",# 全域性預設的指定分頁類,如果檢視想單獨指定,與許可權一樣在view中單獨設定
    "PAGE_SIZE": #每一頁顯示多少資料
    }
 

DRF中的分頁類使用詳解

BasePagination:
分頁的基類,與許可權、限流等原理一樣,寫好模版待後續具體邏輯繼承,略過
47.DRF實現分頁
PageNumberPagination:
繼承BasePagination,這種分頁接收前端請求的頁碼引數,引數是第幾頁則請求第幾頁的資料,例如前端請求page=10,則返回第10頁資料
# settings.py
REST_FRAMEWORK = {
    # 指定分頁類為PageNumberPagination
    "DEFAULT_PAGINATION_CLASS":"rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE":3 #每一頁顯示3條資料
    }
如果是基於通用檢視或者檢視集,當前已經實現了分頁功能
  • 圖1是沒有設定分頁時候的請求,返回全部6條資料
  • 圖2設定了分頁後,預設不傳page引數的請預設返回了第一頁的資料
  • 圖3請求地址後加了page引數,page=2,則返回的是第二頁的資料
  • 也可以看出設定分頁後response多了三個引數count是介面一共的數量,next是下一頁的地址,previous是上一頁地址
47.DRF實現分頁
PageNumberPagination自定義分頁類
上述方式每頁請求的資料量是根據settings設定PAGE_SIZE寫死的數量進行返回,如果我們想要根據傳參定義第幾頁返回多少條資料,可以自定義分頁類
from rest_framework import pagination


# 繼承分頁類
class PublicPagination(pagination.PageNumberPagination):
    page_size = 2  # 每頁顯示的預設資料個數
    page_query_param = 'page'  # 頁號,第幾頁的引數 ,比如定義為pages,那麼請求分頁的引數就應該是pages
    page_size_query_param = 'page_size'  # 自己指定每頁顯示多少個數
    max_page_size = 100  # 最大允許設定的每頁顯示的數量
    
    # last_page_strings用於指定表示請求最後一頁的引數
    # page=last的時候會直接到最後一頁
    # 如果不改引數的話,可以不用設定,不設定的話預設引數就是last
    last_page_strings = 'last' 
''' 
自定義分頁類
透過page_size指定預設的每頁資料量,
page_size_query_param指定每頁自定義的資料量的引數,如果請求page_size=4,則每頁顯示4個,否則走預設的2
max_page_size是允許設定的每頁最大的資料量
'''
# 匯入自定義的分頁類
from .pagination import PublicPagination

class CategoryViewSet(ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    # 指定自定義的分頁類,與許可權、限量等不同,每個檢視只允許指定一個分頁類
    # 對指定檢視設定分頁類,會覆蓋settings中預設的全域性配置
    pagination_class = PublicPagination
上述程式碼實現對應功能後,繼續請求介面
  • 圖1page=2,請求第二頁的資料,返回2條,因為自定義分頁類中的page_size覆蓋了全域性的page_size=3
  • 圖2沒有指定page引數,預設返回第一頁的兩條資料
  • 圖3指定page=2,page_size=1,返回第二頁的資料,用page_size指定1覆蓋程式碼中設定的2,所以只顯示1條資料
47.DRF實現分頁
 
LimitOwsetPagination:
這個分頁類就類似於查詢資料庫中查詢的語法
比如資料庫中 Select * from table limit 100,300,從第101條開始,取300的資料
在分頁中limit用於指定取多少條資料,offset用於指定從多少條開始,與sql一樣,offse+1開始
#view
class CategoryViewSet(ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    # 給該指定LimitOffsetPagination分頁類,也可以在settings指定全域性
    pagination_class = LimitOffsetPagination
下面使用LimitOwsetPagination分頁類請求介面
  • 圖1,沒有傳參返回了3條資料是因為在settings中page_size是3,預設從第一條開始取3條資料,下一頁依次向後取3條
  • 圖2是將settings中的page_size刪除掉,沒有傳遞limit和offset引數就預設全部返回
  • 圖3是傳入limit=2,offset=2,從第三條開始取2條資料
47.DRF實現分頁
LimitOwsetPagination自定義分頁類
# 繼承LimitOffsetPagination分頁類
class PublicLimitOffsetPagination(pagination.LimitOffsetPagination):
    default_limit = 2  # 用於指定預設的limit數量
    limit_query_param = 'lm' # 指定請求時候對應的limit引數名,如果是lm那麼傳參就是lm=
    offset_query_param = 'of' ## 指定請求時候對應的offset引數名,如果是lm那麼傳參就是of=
    max_limit = 4 # 最大的limit可設定數量

CursorPagination:

基於游標的分頁
CursorPagination分頁類說明
  • 顯示一個正向和反向的控制元件,不允許我們任意導航到任意位置
  • 要求結果集中有應該唯一的不變的排序方式
  • 可以確保客戶端在翻頁時不會看到同一物件兩次,即使在分頁的同時有資料插入
  • 對於超級大的資料量,使用前兩個分頁可能會效率低下,基於游標的分頁具有固定的時間屬性,不會因為資料變大而減慢
  • 基於游標的分頁的排序方式預設是使用created排序,如果使用預設排序則模型必須有created時間戳欄位,首先顯示最近新增的資料
  • 可以覆蓋pagination類的ordering屬性,或者使用OrderingFilter過濾器類和CursorPagination來修改排序
  • 使用時要注意應該有一個唯一不變的值,例如預設的created
#settings設定 
REST_FRAMEWORK = {
    # 指定CursorPagination分頁類
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.CursorPagination",
    "PAGE_SIZE": 2  # 每一頁顯示3條資料
    }
 
47.DRF實現分頁

透過游標進行的分頁,可以看出next後面的引數進行了加密,無法直接透過傳遞引數進行跳轉,只能一頁一頁點選
自定義CursorPagination
#繼承CursorPagination分頁類
class PublicCursorPagination(pagination.CursorPagination):
    ordering = '-created'  #透過什麼進行排序,預設created
    page_size = 3 # 每頁資料量
    cursor_query_param = 'cs' #請求的引數欄位,預設cursor
47.DRF實現分頁

相關文章