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

Django筛选中如何高效获取每组值对应的首个对象?

问题分析与解决方案

环境与模型

运行基于PostgreSQL的Django 1.29应用,模型定义如下:

class DataFile(models.Model):
    user = models.ForeignKey('data.User', verbose_name='Data View')
    period = models.ForeignKey('data.Period', verbose_name='Period')
    category = models.ForeignKey('data.Period', verbose_name='Period')
    
    created = models.DateTimeField(auto_now_add=True,null=True)

原低效实现

原代码通过循环遍历pending_files中的(user_id, category_id, period_id)组合,获取每组内创建时间最晚且ID在pending_ids中的对象,但循环导致效率极低:

pending_ids = pending_files.values_list("id", flat=True).distinct()
my_data = pending_files.values_list("user_id","category_id","period_id")

import_files_pending=[]

for u, c, p in pending_bu_way_period:
    last_file_bpw = DataFile.objects.filter(user_id=u, period_id=p,category_id=c).order_by('-created').first()
    if last_file_bpw and last_file_bpw.id in pending_ids:
        import_files_pending.append(last_file_bpw)

重构后的错误实现

尝试重构后返回大量非预期对象,代码如下:

import_files_pending2 = DataFile.objects.filter(id__in=pending_ids,user_id=my_data.values_list("user_id", flat=True),
                                            category_id__in=my_data.values_list("category_id", flat=True),
                                            period_id__in=my_data.values_list("period_id", flat=True)).order_by('user_id',
                                                                                                          'period_id',
                                                                                                         '-created').distinct("from_who_id",
                                                                                                                                   'period_id')

错误原因

  1. 过滤条件逻辑错误:原逻辑是匹配(user_id, category_id, period_id)的组合,但重构代码用三个独立的__in条件,会匹配任意user_id在列表、任意category_id在列表、任意period_id在列表的交叉组合,比如原数据只有(u1,c1,p1),重构后会匹配(u1,c2,p1)这类不存在的组合,导致结果范围大幅扩大。
  2. distinct参数错误:代码中distinct("from_who_id", 'period_id')里的from_who_id并非模型字段,且未包含category_id,无法按原逻辑的三元组分组;同时PostgreSQL的DISTINCT ON要求order_by的前缀必须与distinct的字段完全一致,否则分组逻辑失效。
  3. 未正确关联组合关系:重构代码没有限定只取pending_files中存在的(user_id, category_id, period_id)组合,而是用三个独立__in,导致无关数据被纳入查询。

正确重构方案

方案1:利用PostgreSQL的DISTINCT ON特性(推荐)

PostgreSQL支持DISTINCT ON语法,可按指定字段分组并取每组第一条数据,需确保order_by前缀与distinct字段一致:

import_files_pending2 = DataFile.objects.filter(
    id__in=pending_ids,
    # 仅保留pending_files中存在的三元组组合
    user_id__in=my_data.values_list('user_id', flat=True),
    category_id__in=my_data.values_list('category_id', flat=True),
    period_id__in=my_data.values_list('period_id', flat=True)
).order_by('user_id', 'category_id', 'period_id', '-created').distinct('user_id', 'category_id', 'period_id')
  • 逻辑:先按user_id、category_id、period_id分组,每组内按created倒序排列,DISTINCT ON会保留每组的第一条数据(即创建时间最晚的)。

方案2:使用子查询获取每组最新ID

通过子查询预先获取每个三元组组合对应的最新ID,再查询这些ID的对象:

from django.db.models import Subquery, OuterRef

# 子查询:获取每个(user_id, category_id, period_id)组合下,pending_ids中创建时间最晚的ID
latest_file_ids = DataFile.objects.filter(
    user_id=OuterRef('user_id'),
    category_id=OuterRef('category_id'),
    period_id=OuterRef('period_id'),
    id__in=pending_ids
).order_by('-created').values('id')[:1]

# 主查询:筛选符合条件的对象
import_files_pending2 = DataFile.objects.filter(
    id__in=Subquery(latest_file_ids),
    user_id__in=my_data.values_list('user_id', flat=True),
    category_id__in=my_data.values_list('category_id', flat=True),
    period_id__in=my_data.values_list('period_id', flat=True)
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:32:01