資料推薦
官方文檔
https://q1mi.github.io/Django-REST-framework-documentation/api-guide/pagination_zh/
B站沒有好的教學視頻,不建議看,直接看官方文檔吧。
PageNumberPagination
此分頁樣式接受請求查詢參數中的單個數字頁碼。
Request:
GET https://api.example.org/accounts/?page=4
Response:
{"count": 1023"next": "https://api.example.org/accounts/?page=5","previous": "https://api.example.org/accounts/?page=3","results": […]
}
Settings:
全局設置
全局啟用 PageNumberPagination
樣式,請使用以下配置,并根據需要設置 PAGE_SIZE
:
REST_FRAMEWORK = {'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination','PAGE_SIZE': 100
}
單視圖設置(重點!)
自定義繼承子類
自定義一個繼承自PageNumberPagination的分頁器,然后在里面設置page_size,如下:
class LargeResultsSetPagination(PageNumberPagination):page_size = 1000page_size_query_param = 'page_size' # 非必需max_page_size = 10000 # 非必需
重寫get_paginate_by方法
如果只想用PageNumberPagination本身,那么需要通過重寫get_paginate_by方法來設置page_size。
class YourView(generics.ListAPIView):queryset = YourModel.objects.all()serializer_class = YourSerializer# 使用官方的 PageNumberPagination 類pagination_class = PageNumberPagination# 覆蓋默認的分頁設置def get_paginate_by(self, queryset):return self.request.query_params.get('page_size', 5)
LimitOffsetPagination
這種分頁樣式反映了查找多個數據庫記錄時使用的語法。客戶端包括“limit”和“offset”查詢參數。limit指示要返回的項目的最大數目,這相當于其他樣式中的 page_size
。offset指示查詢相對于完整的未分頁項的起始位置。
Request:
GET https://api.example.org/accounts/?limit=100&offset=400
Response:
HTTP 200 OK
{"count": 1023"next": "https://api.example.org/accounts/?limit=100&offset=500","previous": "https://api.example.org/accounts/?limit=100&offset=300","results": […]
}
Settings:
全局設置
為了全局啟用 LimitOffsetPagination
樣式,請使用以下配置:
REST_FRAMEWORK = {'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination','PAGE_SIZE': 10
}
ps:又是一個易錯點,雖然limitoffsetpagination用的是limit,但是全局設置時還是要設置PAGE_SIZE。。
單視圖設置(重點!)
自定義繼承子類實現
自定義一個繼承自LimitOffsetPagination
的分頁器,然后在里面設置limit,如下:
class CustomLimitOffsetPagination(LimitOffsetPagination):default_limit = 5 # 自定義默認每頁顯示的數量,一定要給max_limit = 20 # 自定義每頁顯示的最大數量,非必需
重寫get_paginate_by
如果只想用LimitOffsetPagination本身,那么需要通過重寫get_paginate_by方法來設置limit。
from rest_framework import generics
from rest_framework.pagination import LimitOffsetPaginationclass YourView(generics.ListAPIView):queryset = YourModel.objects.all()serializer_class = YourSerializerpagination_class = LimitOffsetPagination # 使用 LimitOffsetPagination# 覆蓋默認的分頁設置def get_paginate_by(self, queryset):return self.request.query_params.get('limit', 10)
獲取分頁輸出樣式/具體如何使用分頁器(重點!)
像是如上的
{"count": 1023"next": "https://api.example.org/accounts/?page=5","previous": "https://api.example.org/accounts/?page=3","results": […]
}
{"count": 1023"next": "https://api.example.org/accounts/?limit=100&offset=500","previous": "https://api.example.org/accounts/?limit=100&offset=300","results": […]
}
都是pagenumberpagination、limitoffsetpagination等官方分頁器通過get_paginated_response
返回的默認的分頁樣式。
具體使用如下:
class CustomLimitOffsetPagination(LimitOffsetPagination):default_limit = 5 # 自定義默認每頁顯示的數量class YourView(generics.ListAPIView):queryset = YourModel.objects.all()serializer_class = YourSerializerpagination_class = CustomLimitOffsetPagination # 使用自定義分頁類def get(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)
自定義分頁輸出樣式(重寫get_paginated_response方法!!重要!)
假設我們想用一個修改過的格式替換默認的分頁輸出樣式,該格式在嵌套的“links”鍵中包含下一頁和上一頁的鏈接。我們就需要自定義分頁器,并重寫get_paginated_response方法
class CustomPagination(pagination.PageNumberPagination):def get_paginated_response(self, data):return Response({'links': {'next': self.get_next_link(),'previous': self.get_previous_link()},'count': self.page.paginator.count,'results': data})
最常使用場景舉例
from rest_framework.pagination import LimitOffsetPaginationfrom utils import ThirdPartResponseclass TaskPagination(LimitOffsetPagination):default_limit = 5 # 自定義默認每頁顯示的數量,一定要給。及時后續會在請求參數中拿到limit,也一定要給def get_paginated_response(self, data):return ThirdPartResponse(data={"total": self.count, "data": data})@action(detail=False, methods=["get"], url_path="list-by-template/(?P<template_id>.+)")
def list_by_template(self, request, template_id=None):"""根據template_id過濾任務列表"""if template_id is None:message = "template_id is required"logger.error(message)return ThirdPartResponse(result=False, message=message)tasks = self.queryset.filter(template_id=template_id)paginator = TaskPagination()page = paginator.paginate_queryset(tasks, request)if page is not None:serializer = self.get_serializer(page, many=True)return paginator.get_paginated_response(serializer.data)serializer = self.get_serializer(tasks, many=True)return Response(serializer.data)