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 ] } ]
问题原因及修复方案
核心问题
- 分页类不兼容:settings配置的是依赖
page参数的PageNumberPagination,但视图里手动实例化了依赖limit/offset参数的LimitOffsetPagination,导致传入的page参数无法触发正确的分页逻辑。 - 视图逻辑冗余:先序列化全量数据,后续分页处理属于重复操作,且逻辑顺序可能导致部分场景下分页不执行。
修复步骤
方案一:沿用配置的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
相关产品推荐
相关产品推荐

