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

Django中按外键去重获取单个关联Answer对象的QuerySet方法

问题:按Survey外键去重获取Answer QuerySet

模型定义

class Answer(models.Model):
   survey = models.ForeignKey(Survey)

需求

根据Survey外键获取Answer的QuerySet,要求每个Survey仅对应一个Answer对象。例如现有3个Answer对象:

answers = [
    {"survey": 1},
    {"survey": 2}, 
    {"survey": 1},
  ]

期望返回的QuerySet为:

[
    {"survey": 2}, 
    {"survey": 1},
  ]

即同一外键对应的Answer仅保留一个。

尝试过的无效方法

def get_queryset(self, request):
        qs = super().get_queryset(request)
        idx = list(qs.values_list("survey_id", flat=True).distinct())

        return qs.filter(survey_id__in=idx).distinct()

已实现的方案(寻求优化)

def get_queryset(self, request):
        qs = super().get_queryset(request)
        idx = list(qs.values_list("survey_id", flat=True).distinct())

        data = []
        for i in qs:
            if i.survey_id in idx:
                data.append(i)
                idx.remove(i.survey_id)

        # 转换为QuerySet
        return qs.filter(pk__in=[i.pk for i in data])

优化方案建议

方案1:PostgreSQL专属distinct_on(高效简洁)

如果使用PostgreSQL数据库,可以直接利用distinct_on特性按survey_id去重,保留每个Survey对应的第一条记录(可通过排序字段控制保留哪一条,比如按创建时间):

def get_queryset(self, request):
    qs = super().get_queryset(request)
    # 按survey_id分组去重,保留每个survey的第一个记录(可调整order_by字段选择保留的对象)
    return qs.order_by('survey_id', 'id').distinct('survey_id')

方案2:跨数据库通用的Subquery方式

如果需要兼容多数据库,可以用Subquery获取每个Survey对应的最新/最早Answer:

from django.db.models import Subquery, OuterRef

def get_queryset(self, request):
    qs = super().get_queryset(request)
    # 子查询获取每个survey对应的最新Answer的id(按id倒序取第一个)
    latest_answer_ids = Answer.objects.filter(survey=OuterRef('survey')).order_by('-id').values('id')[:1]
    # 过滤出这些目标Answer
    return qs.filter(id__in=Subquery(latest_answer_ids))

方案3:优化现有遍历逻辑

如果不想修改查询逻辑,也可以优化现有代码,避免不必要的内存开销和列表操作:

def get_queryset(self, request):
    qs = super().get_queryset(request)
    # 用字典推导式快速获取每个survey_id对应的一个Answer id(按遍历顺序取第一个)
    unique_answer_map = {ans.survey_id: ans.id for ans in qs.order_by('survey_id')}
    # 过滤出这些id对应的Answer
    return qs.filter(id__in=unique_answer_map.values())

这个方法通过字典的键唯一性自动去重,比原方案的列表遍历+移除操作更高效。

内容的提问来源于stack exchange,提问作者Iqra Moazzama

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 03:40:53