DRF自定义QuerySet未按预期重新评估的问题咨询
问题结论
这是DRF的预期工作行为,问题根源不在DRF的实现逻辑,而在自定义QuerySet的执行特性和Django类属性的加载时机。
原因说明
- Django类视图的类属性会在模块首次加载阶段就完成计算,并非每次请求时动态计算。
- 你自定义的
world_rankings方法中调用了.first(),这是会立即触发SQL执行的操作:当你把queryset = Ranking.objects.world_ranking()作为类属性赋值时,world_ranking方法会在类加载阶段直接运行,此时你的测试数据还未通过setUpTestData插入,所以拿到的update为空,返回的是固定的空QuerySet。 - DRF默认
get_queryset方法中调用.all()的作用,是让未执行的惰性QuerySet重新拉取数据,但是你赋值给queryset属性的已经是执行后返回的空QuerySet,后续无论怎么调用.all()都只会基于已经确定的过滤条件返回空结果。 - 你换成普通filter操作能正常运行的原因是:普通filter不会触发立即执行,类加载阶段只是生成了带过滤规则的惰性QuerySet,直到请求处理时才会真正执行SQL,自然能拿到后来插入的测试数据。
优化方案
除了自定义get_queryset(这也是DRF官方推荐的动态QuerySet标准写法)之外,你也可以修改自定义QuerySet方法,去掉立即执行的操作,让整个查询变成完全惰性的,就可以直接赋值给类属性使用:
class RankingQuerySet(models.QuerySet): def world_rankings(self, rank_type="World"): # 用子查询替代first(),避免立即触发SQL执行 latest_update_subquery = RankingUpdate.objects.issued().filter( rank_type=rank_type, date__lte=TODAY ).order_by("-asat", "-date").values("id")[:1] return self.filter( update_id__in=latest_update_subquery, player_id__gt=0, points__gt=0 )
修改后原来的视图写法不需要变动,每次请求都会自动重新执行查询拿到最新数据。
内容的提问来源于stack exchange,提问作者PhoebeB
相关产品推荐
相关产品推荐

