DRF CursorPagination多排序字段与过滤组合致上一页空结果问题
DRF CursorPagination 结合过滤器时返回空previous页的问题解决
问题现象
使用Django Rest Framework(DRF)的CursorPagination搭配过滤器时出现异常:访问端点初始页能获取到next URL,访问该next URL后会生成previous URL,但点击这个previous URL却返回空的结果列表。
排查发现:虽然分页类中配置了多字段排序(date, custom_channel_name, country_name, platform_type_code, id),但解码后的previous游标仅包含date字段;查看DRF源码后确认,默认的_get_position_from_instance方法只提取排序配置中的第一个字段值。当存在重复的date值时,过滤后的分页定位逻辑会出错,导致previous页无法正确匹配数据。
用户相关代码:
class RevenuePagination(CursorPagination): page_size = 200 ordering = ['date', 'custom_channel_name', 'country_name', 'platform_type_code', 'id'] class RevenueFilter(django_filters.FilterSet): class Meta: model = Revenue fields = { 'date': ['lte', 'gte'], 'custom_channel_name': ['exact'], 'country_name': ['exact'], 'platform_type_code': ['exact'], } class RevenueViewSet(viewsets.ModelViewSet): permission_classes = [HasAPIKey] queryset = Revenue.objects.all() serializer_class = RevenueSerializer filterset_class = RevenueFilter pagination_class = RevenuePagination @revenue_list_schema() def list(self, request, *args, **kwargs): return super().list(request, *args, **kwargs)
解决方案
重写CursorPagination的_get_position_from_instance方法,让它收集所有排序字段的值,而不是只取第一个。这样即使date字段有重复,后续的唯一字段(比如id)能确保游标准确定位,避免分页逻辑出错。
修改后的分页类代码:
class RevenuePagination(CursorPagination): page_size = 200 ordering = ['date', 'custom_channel_name', 'country_name', 'platform_type_code', 'id'] def _get_position_from_instance(self, instance): position = [] for field in self.ordering: # 处理降序排序的情况(如果排序字段带'-'前缀) if field.startswith('-'): field_name = field[1:] # 数值型字段取反,非数值型直接取值(可根据实际场景调整) field_value = getattr(instance, field_name) value = -field_value if isinstance(field_value, (int, float)) else field_value else: value = getattr(instance, field) position.append(value) return position
说明
- 上述代码会遍历所有配置的排序字段,将每个字段的值都加入到游标位置中,确保游标能唯一标识数据行的位置。
- 如果排序字段包含外键或其他需要特殊处理的字段,需要调整
getattr的取值逻辑,比如外键字段可以取instance.foreign_key.id来获取主键值。
内容的提问来源于stack exchange,提问作者Martin Taleski
相关产品推荐
相关产品推荐

