如何在Django ORM中筛选至少有一个回答的问题?
获取至少有一个回答的问题的最优方案
嘿,我来帮你搞定这个需求!根据你给出的ChallengeQuestionAnswer模型结构,要找出至少有一个回答的问题,最高效的方式是利用Django的Exists子查询——毕竟我们只需要确认存在回答就行,完全不用统计具体数量,这种方法在数据量大的时候性能优势特别明显。
核心思路
你的模型里,original_question是自关联外键,related_name='answers'意味着每个原始问题(original_question为null的条目)的回答都会被归类到它的answers集合里。我们要找的就是那些answers集合非空的原始问题。
最优实现:Exists子查询
from django.db.models import Exists, OuterRef # 筛选所有有至少一个回答的原始问题 questions_with_answers = ChallengeQuestionAnswer.objects.filter( original_question__isnull=True, # 只选原始提问(不是回答本身) Exists( # 检查是否存在关联的回答条目 ChallengeQuestionAnswer.objects.filter(original_question=OuterRef('pk')) ) )
为什么说这是最优?因为Exists只要找到第一条符合条件的回答就会停止查询,不像用Count('answers')那样要遍历所有关联记录计数,数据量越大,性能差距越明显。
扩展:如果需要包含有追问的回答
如果你要的不只是原始提问,还包括那些本身是回答但又被追问的条目(也就是任何有后续回复的内容),只需要去掉original_question__isnull=True这个过滤条件就行:
questions_with_answers = ChallengeQuestionAnswer.objects.filter( Exists( ChallengeQuestionAnswer.objects.filter(original_question=OuterRef('pk')) ) )
复用技巧:自定义模型管理器
如果这个查询你会频繁用到,可以给模型加个自定义管理器,以后调用起来更方便:
from django.db import models from django.db.models import Exists, OuterRef from django.conf import settings class ChallengeQuestionAnswerManager(models.Manager): def with_answers(self, only_original=True): queryset = self.all() if only_original: queryset = queryset.filter(original_question__isnull=True) return queryset.filter( Exists( ChallengeQuestionAnswer.objects.filter(original_question=OuterRef('pk')) ) ) class ChallengeQuestionAnswer(models.Model): """基础问答论坛线程""" challenge = models.ForeignKey('Challenge', on_delete=models.CASCADE) original_question = models.ForeignKey('self', blank=True, null=True, related_name='answers') creator = models.ForeignKey(settings.AUTH_USER_MODEL) creation_date = models.DateTimeField(auto_now_add=True) # 补全常见默认值 objects = ChallengeQuestionAnswerManager()
之后你就可以直接这样调用:
# 只获取有回答的原始提问 ChallengeQuestionAnswer.objects.with_answers() # 获取所有有后续回复的内容(包括回答被追问的情况) ChallengeQuestionAnswer.objects.with_answers(only_original=False)
避坑提醒
千万别用annotate(answer_count=Count('answers')).filter(answer_count__gt=0)这种方式,性能差很多;也不要在循环里用if question.answers.exists(),会触发N+1查询,拖慢整个系统。
内容的提问来源于stack exchange,提问作者Encompass
相关产品推荐
相关产品推荐

