DRF 分页 #

一、分页概述 #

1.1 为什么需要分页 #

当数据量很大时,分页可以:

  • 减少网络传输
  • 提高响应速度
  • 降低服务器负载
  • 改善用户体验

1.2 分页类型 #

类型 说明 适用场景
PageNumberPagination 页码分页 传统分页
LimitOffsetPagination 偏移分页 灵活分页
CursorPagination 游标分页 大数据集

二、PageNumberPagination #

2.1 全局配置 #

python
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
}

2.2 自定义分页类 #

python
from rest_framework.pagination import PageNumberPagination

class StandardPagination(PageNumberPagination):
    page_size = 20
    page_size_query_param = 'page_size'
    max_page_size = 100
    page_query_param = 'page'

class LargePagination(PageNumberPagination):
    page_size = 50
    max_page_size = 200

2.3 视图中使用 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    pagination_class = StandardPagination

2.4 请求示例 #

text
GET /api/articles/?page=2
GET /api/articles/?page=2&page_size=20

2.5 响应格式 #

json
{
    "count": 100,
    "next": "http://api.example.com/articles/?page=3",
    "previous": "http://api.example.com/articles/?page=1",
    "results": [...]
}

三、LimitOffsetPagination #

3.1 配置 #

python
from rest_framework.pagination import LimitOffsetPagination

class CustomLimitOffsetPagination(LimitOffsetPagination):
    default_limit = 10
    limit_query_param = 'limit'
    offset_query_param = 'offset'
    max_limit = 100

3.2 使用 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    pagination_class = CustomLimitOffsetPagination

3.3 请求示例 #

text
GET /api/articles/?limit=10&offset=20
GET /api/articles/?limit=5

3.4 响应格式 #

json
{
    "count": 100,
    "next": "http://api.example.com/articles/?limit=10&offset=30",
    "previous": "http://api.example.com/articles/?limit=10&offset=10",
    "results": [...]
}

四、CursorPagination #

4.1 配置 #

python
from rest_framework.pagination import CursorPagination

class ArticleCursorPagination(CursorPagination):
    page_size = 20
    cursor_query_param = 'cursor'
    ordering = '-created_at'

4.2 使用 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    pagination_class = ArticleCursorPagination

4.3 请求示例 #

text
GET /api/articles/
GET /api/articles/?cursor=cD0yMDI0LTAxLTAx

4.4 响应格式 #

json
{
    "next": "http://api.example.com/articles/?cursor=cD0yMDI0LTAxLTAx",
    "previous": null,
    "results": [...]
}

4.5 优势 #

  • 不显示总数,性能更好
  • 适合无限滚动
  • 数据一致性更好

五、自定义分页 #

5.1 自定义响应格式 #

python
from rest_framework.pagination import PageNumberPagination
from rest_framework.response import Response

class CustomPagination(PageNumberPagination):
    page_size = 10
    
    def get_paginated_response(self, data):
        return Response({
            'success': True,
            'data': data,
            'pagination': {
                'total': self.page.paginator.count,
                'page': self.page.number,
                'page_size': self.page_size,
                'total_pages': self.page.paginator.num_pages,
                'has_next': self.page.has_next(),
                'has_previous': self.page.has_previous(),
            }
        })

5.2 全局配置 #

python
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'core.pagination.CustomPagination',
    'PAGE_SIZE': 10,
}

5.3 响应格式 #

json
{
    "success": true,
    "data": [...],
    "pagination": {
        "total": 100,
        "page": 1,
        "page_size": 10,
        "total_pages": 10,
        "has_next": true,
        "has_previous": false
    }
}

六、禁用分页 #

6.1 视图级别 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    pagination_class = None

6.2 action级别 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    @action(detail=False, pagination_class=None)
    def all(self, request):
        articles = self.get_queryset()
        serializer = self.get_serializer(articles, many=True)
        return Response(serializer.data)

七、分页最佳实践 #

7.1 选择合适的分页类型 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    def get_pagination_class(self):
        if self.action == 'list':
            return StandardPagination
        elif self.action == 'timeline':
            return CursorPagination
        return None

7.2 优化查询 #

python
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.select_related(
        'author', 'category'
    ).prefetch_related(
        'tags'
    ).only(
        'id', 'title', 'summary', 'created_at'
    )
    serializer_class = ArticleSerializer

7.3 缓存计数 #

python
from django.core.cache import cache

class CachedPagination(PageNumberPagination):
    def get_count(self, queryset):
        cache_key = f'article_count_{queryset.query}'
        count = cache.get(cache_key)
        
        if count is None:
            count = super().get_count(queryset)
            cache.set(cache_key, count, 60 * 5)
        
        return count

八、总结 #

本章学习了DRF分页:

  • PageNumberPagination:传统页码分页
  • LimitOffsetPagination:偏移分页
  • CursorPagination:游标分页
  • 自定义分页:自定义响应格式
  • 最佳实践:选择和优化

让我们继续学习过滤与排序!

最后更新:2026-03-28