Django中如何无需转列表用PageNumberPagination分页RawQuerySet
在Django中实现RawQuerySet的PageNumber分页(无需全量转列表)
要解决RawQuerySet无法直接使用PageNumberPagination的问题,核心是补充分页所需的总记录数计算和惰性切片逻辑,以下是具体实现方案:
自定义兼容RawQuerySet的分页类
继承DRF的PageNumberPagination,重写分页逻辑以适配RawQuerySet:
from rest_framework.pagination import PageNumberPagination from django.db import connection class RawQuerySetPagination(PageNumberPagination): page_size = 15 page_query_param = 'page' def paginate_queryset(self, queryset, request, view=None): # 1. 高效计算总记录数(避免全量加载) # 基于原始SQL生成count查询,封装为子查询 count_sql = f"SELECT COUNT(*) FROM ({queryset.raw_query}) AS sub_query" with connection.cursor() as cursor: cursor.execute(count_sql) total_count = cursor.fetchone()[0] # 初始化分页器的总数和总页数属性 self.paginator.count = total_count self.paginator.num_pages = (total_count + self.page_size - 1) // self.page_size # 2. 解析当前页码,计算偏移量 page_num = request.query_params.get(self.page_query_param, 1) try: page_num = max(int(page_num), 1) except ValueError: page_num = 1 offset = (page_num - 1) * self.page_size end_offset = offset + self.page_size # 3. 对RawQuerySet进行惰性切片(仅加载当前页数据) paginated_data = queryset[offset:end_offset] # 设置分页上下文 self.page = self.paginator.page(page_num) self.request = request return list(paginated_data)
在视图中使用自定义分页类
替换原代码中的分页逻辑,使用自定义类处理RawQuerySet:
# 视图中的示例代码 queryset = CheckTraModelTotal.objects.raw(sql_query) paginator = RawQuerySetPagination() page_data = paginator.paginate_queryset(queryset, self.request) serializer = self.get_serializer(page_data, many=True) return paginator.get_paginated_response(serializer.data)
关键注意事项
- 禁止用
len(queryset)计算总数:这会触发全量SQL查询并加载所有数据到内存,严重影响性能。必须用原生count子查询实现高效计数。 - RawQuerySet切片是惰性的:
queryset[offset:end_offset]会自动在原始SQL后拼接LIMIT/OFFSET,仅查询当前页数据,无需全量转换为列表。 - 清理原始SQL中的LIMIT/OFFSET:如果你的
sql_query已经包含分页相关语句,需要先移除,避免和分页器的逻辑冲突。
内容的提问来源于stack exchange,提问作者Abdul Rahman.
相关产品推荐
相关产品推荐

