分頁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:
分頁的基類,與許可權、限流等原理一樣,寫好模版待後續具體邏輯繼承,略過
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是上一頁地址
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條資料
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條資料
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條資料
}
透過游標進行的分頁,可以看出next後面的引數進行了加密,無法直接透過傳遞引數進行跳轉,只能一頁一頁點選
自定義CursorPagination
#繼承CursorPagination分頁類
class PublicCursorPagination(pagination.CursorPagination):
ordering = '-created' #透過什麼進行排序,預設created
page_size = 3 # 每頁資料量
cursor_query_param = 'cs' #請求的引數欄位,預設cursor