Django ManyToMany查询导出CSV仅返回聚合计数避免重复行
问题根因
调用values_list()时传入多对多字段likes,Django会对新闻表和点赞关联表做JOIN查询,每存在一条用户点赞的关联记录就会返回一行结果,因此会出现单条新闻重复多行、对应列显示点赞用户ID而非总数的问题。你模型中已经定义了计算单条新闻点赞数的total_likes属性,只要避开直接拉取多对多字段值即可解决问题。
方案1:直接复用模型属性(写法简单,适配小数据量场景)
不要通过values_list()拉取likes字段,直接遍历News查询集,调用已定义的total_likes属性取值即可,修改后的代码如下:
@login_required def export_news(request): newss = News.objects.all().prefetch_related('likes') response = HttpResponse(content_type='text/csv') writer = csv.writer(response) writer.writerow(["ID","Title","Author","Date","Text","Likes Count"]) for news in newss: writer.writerow([ news.id, news.news_title, news.news_author, news.news_date, news.news_text, news.total_likes ]) response['Content-Disposition'] = 'attachment; filename="News_list.csv"' return response
- 代码中加入
prefetch_related('likes')可以一次性预加载所有新闻关联的点赞数据,避免循环过程中每条新闻单独触发一次点赞计数查询,大幅降低数据库压力。 - 顺手修正了原代码中不标准的
txt/csvMIME类型,替换为标准的text/csv,避免部分浏览器/下载工具识别文件类型异常。
方案2:数据库聚合统计(性能更优,适配大数据量场景)
如果新闻数据量较大,可以直接在数据库层完成点赞数聚合计算,不需要把全量点赞用户数据加载到Python内存,执行效率更高:
from django.db.models import Count @login_required def export_news(request): newss = News.objects.annotate(total_likes=Count('likes')).values_list( 'id','news_title','news_author','news_date','news_text','total_likes' ) response = HttpResponse(content_type='text/csv') writer = csv.writer(response) writer.writerow(["ID","Title","Author","Date","Text","Likes Count"]) for news in newss: writer.writerow(news) response['Content-Disposition'] = 'attachment; filename="News_list.csv"' return response
- 该写法天然不会产生重复行,聚合查询会按新闻主键分组统计,返回结果中每条新闻仅对应一行,点赞数由数据库直接计算返回,数据量越大性能优势越明显。
内容的提问来源于stack exchange,提问作者Jonasz
相关产品推荐
相关产品推荐

