使用prefetch_related与聚合解决Django时序数据模型查询N+1问题
优化方案
你只需要直接从Vote模型出发做分组聚合,就能实现单条查询拿到所有需要的结果,完全解决N+1性能问题:
第一步:导入依赖
在views.py开头引入需要的Django数据库工具:
from django.db.models import Max, Min from django.db.models.functions import TruncDate
第二步:重写视图逻辑
把原来的循环查询逻辑替换为下面的代码:
def index(request): # 单条查询完成所有按建议、按日期的分组聚合计算 vote_agg_data = Vote.objects.values( "suggestion__title", vote_date = TruncDate("timestamp") ).annotate( daily_new_votes = Max("votes") - Min("votes") ).order_by("suggestion__title", "vote_date") # 整理成你需要的字典结构,完全在Python内存中处理,无额外数据库查询 votes_per_day_per_suggestion = {} for item in vote_agg_data: title = item["suggestion__title"] vote_date = item["vote_date"] if title not in votes_per_day_per_suggestion: votes_per_day_per_suggestion[title] = {} votes_per_day_per_suggestion[title][vote_date] = item["daily_new_votes"] # 如果需要展示没有任何投票记录的建议,补充下面这段逻辑(只会多1次查询) # for title in Suggestion.objects.values_list("title", flat=True): # if title not in votes_per_day_per_suggestion: # votes_per_day_per_suggestion[title] = {} context = {"votes_per_day_per_suggestion": votes_per_day_per_suggestion} return render(request, "borgerforslag/index.html", context)
方案说明
- 整个逻辑最多只会执行1-2次数据库查询,和原来的N+1查询相比性能提升非常明显,数据量越大优势越突出
- 所有聚合计算都在数据库层完成,比Python循环逐行处理效率更高
- 模板层不需要做任何修改,完全兼容你原来的输出逻辑
内容的提问来源于stack exchange,提问作者Morten
相关产品推荐
相关产品推荐

