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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:24:20