You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django REST API View装饰器分页异常:切换页码返回相同数据

问题描述

在Django REST框架中实现分页功能时,切换页码(如访问http://localhost:8000/movies?page=3)始终返回相同的响应数据,相关配置和代码如下:

视图代码

@api_view(['GET', 'POST', 'DELETE', 'PUT'])
def movies(request):
    if request.method == 'GET':
        if request.query_params.get('id'):
            try:
                id = request.query_params.get('id')
                movie = Movie.objects.get(id=id)
                serializer = MovieSerializer(movie, many=False)
                return Response(serializer.data)
            except Movie.DoesNotExist:
                return Response(status=status.HTTP_404_NOT_FOUND)

        movies = Movie.objects.all().order_by('release_date')
        serializer = MovieSerializer(movies , many=True, context={'request':request})

        if request.query_params.get('page'):
            paginator = LimitOffsetPagination()
            result_page = paginator.paginate_queryset(movies, request)
            serializer = MovieSerializer(result_page, many=True, context={'request':request})
            return Response(serializer.data)

        if request.query_params.get('Genre'):
            genreparam = request.query_params.get('Genre')
            genre = Genre.objects.get(name=genreparam)
            queryset = Movie.objects.filter(genre_relation=genre.id).values().order_by('release_date')
            return Response(queryset)
        return Response(serializer.data)

settings.py配置

REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 2,
}

固定返回的响应数据

[
{
    "id": 1,
    "title": "Guardians of the galaxy",
    "tagline": "this is a tagline",
    "overview": "this is an overview, starlord in the begins...",
    "release_date": "1971-07-13T03:00:00Z",
    "poster_url": "http\"//posterurl",
    "backdrop_url": "http\"//backdropurl",
    "imdb_id": "idk what is a imdb",
    "genre_relation": []
},
{
    "id": 2,
    "title": "Avengers endgame",
    "tagline": "this is a tagline",
    "overview": "tony stark dies, theres no more happy days, only days",
    "release_date": "2019-07-13T03:00:00Z",
    "poster_url": "http//posterurl",
    "backdrop_url": "http//backdropurl",
    "imdb_id": "idk what is a imdb",
    "genre_relation": [
        1
    ]
}
]
问题原因及修复方案

核心问题

  1. 分页类不兼容:settings配置的是依赖page参数的PageNumberPagination,但视图里手动实例化了依赖limit/offset参数的LimitOffsetPagination,导致传入的page参数无法触发正确的分页逻辑。
  2. 视图逻辑冗余:先序列化全量数据,后续分页处理属于重复操作,且逻辑顺序可能导致部分场景下分页不执行。

修复步骤

方案一:沿用配置的PageNumberPagination

修改视图逻辑,统一使用配置的分页类,同时优化查询顺序:

from rest_framework.pagination import PageNumberPagination

@api_view(['GET', 'POST', 'DELETE', 'PUT'])
def movies(request):
    if request.method == 'GET':
        # 单个电影查询逻辑保留
        if request.query_params.get('id'):
            try:
                id = request.query_params.get('id')
                movie = Movie.objects.get(id=id)
                serializer = MovieSerializer(movie, many=False)
                return Response(serializer.data)
            except Movie.DoesNotExist:
                return Response(status=status.HTTP_404_NOT_FOUND)
        
        # 初始化基础查询集
        queryset = Movie.objects.all().order_by('release_date')
        
        # 先处理分类筛选
        genre_param = request.query_params.get('Genre')
        if genre_param:
            try:
                genre = Genre.objects.get(name=genre_param)
                queryset = queryset.filter(genre_relation=genre.id)
            except Genre.DoesNotExist:
                return Response(status=status.HTTP_404_NOT_FOUND)
        
        # 统一执行分页(自动识别page参数)
        paginator = PageNumberPagination()
        paginated_queryset = paginator.paginate_queryset(queryset, request)
        serializer = MovieSerializer(paginated_queryset, many=True, context={'request':request})
        # 返回带分页导航链接的标准响应
        return paginator.get_paginated_response(serializer.data)

方案二:改用LimitOffsetPagination

如果要保留LimitOffsetPagination,需修改settings配置,同时前端需要传limit和offset参数(如?offset=4&limit=2获取第3页数据):

# 修改settings中的默认分页类
REST_FRAMEWORK = {
    'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 2,
}

视图中可直接依赖默认分页逻辑,无需手动实例化分页器。

额外优化点

  • 增加Genre.DoesNotExist异常捕获,避免分类参数错误时直接报错。
  • 调整逻辑顺序,先处理筛选再执行分页,减少重复数据库查询。
  • 使用get_paginated_response返回,自动包含分页导航链接(上一页、下一页、总数据量等)。

内容的提问来源于stack exchange,提问作者Lucas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 14:30:41