Django 中如何获取按分类分组、含关联文章列表的结构化queryset
按分类维度聚合关联文章的Django ORM实现
前置模型修正
现有模型存在两处容易触发运行错误的小问题,实现查询前建议先调整:
- person模型里关联Interest的外键拼写为
Ineterest,属于笔误,需修正为Interest - 两个ManyToManyField字段的
default=None配置不符合Django字段规则:多对多字段不支持设置默认值,保留该参数会在服务启动时报错,建议直接移除该参数
核心实现方案(无N+1查询,性能最优)
利用prefetch_related预取关联数据,总共仅需2次数据库查询即可拿到全量匹配结果,避免循环内逐分类查库的性能损耗:
from django.db import models from your_app.models import Interest, Article # 1. 预取每个Interest分类下关联的所有Article interest_list = Interest.objects.prefetch_related( models.Prefetch( # Article模型category字段的反向默认关联名 'article_set', # 此处可追加文章的过滤条件,比如仅拉取已发布的文章 queryset=Article.objects.all(), # 将预取的结果绑定到指定属性,避免和默认反向管理器混淆 to_attr='related_articles' ) ).all() # 2. 组装成目标结构格式 target_result = [] for interest in interest_list: target_result.append({ # 分类名取Interest模型中interest字段的存储值,比如'Finance' 'category': interest.interest, # 转成列表存放对应分类下的所有文章对象 'articles': list(interest.related_articles) })
常用场景适配调整
- 过滤空分类:如果不需要展示没有关联任何文章的分类,可在初始查询链上追加注解过滤:
interest_list = Interest.objects.annotate( article_total=models.Count('article_set') ).filter( article_total__gt=0 ).prefetch_related(...) # 后续预取逻辑和上面保持一致 - 文章排序需求:如果需要每个分类下的文章按发布时间/热度排序,直接在
Prefetch的queryset参数里追加排序规则即可,比如queryset=Article.objects.order_by('-publish_time') - 精简文章返回内容:如果不需要完整的Article对象,只需要存文章ID/标题,可在组装结构时直接取对应字段值,无需将整个模型对象塞入列表。
注意:不推荐在循环内逐次调用
interest.article_set.all()的写法,这种写法会触发N+1次数据库查询,当分类规模较大时会带来明显的数据库压力。
内容的提问来源于stack exchange,提问作者Muhammad Hammad
相关产品推荐
相关产品推荐

