You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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/csv MIME类型,替换为标准的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 04:27:25