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

Django ORM基于指定字段获取去重记录失败问题排查

问题原因分析

你的distinct('first_name', 'last_name', 'date_of_birth')无效,核心原因有两点:

  1. union的去重逻辑:union默认会基于所有选定字段进行去重,只要任意一个字段值不同(比如你这里的source字段,一个是'booking'、一个是'patient'),就会被视为两条独立记录保留下来,所以即使三个标识字段相同,这两条记录在union后依然共存。
  2. distinct(*fields)的局限性:在ValuesQuerySet中使用distinct(*fields),并不会忽略其他字段的差异——它仅保证返回的记录中指定字段的组合唯一,但如果两条记录指定字段相同但其他字段不同,数据库无法确定返回哪一条,且无法满足你“优先保留source=patient”的需求。
解决方案

针对你的需求(当两个模型存在相同first_name/last_name/date_of_birth的记录时,保留source=patient的那条),推荐以下两种实现方式:

方案一:使用窗口函数(高效,支持大数据量)

利用Django的窗口函数RowNumber,按三个标识字段分组,将source='patient'的记录排在每组首位,最后筛选出每组的第一条记录即可。

修改后的代码:

from django.db.models import Window, F, Q, Value
from django.db.models.functions import RowNumber
from django.db.models import ExpressionWrapper, DateTimeField
from datetime import datetime, timedelta

def get_queryset(self):
    current_date = datetime.today()

    # 构建符合条件的booking数据集
    booking_data = PatientBooking.objects.annotate(
        admission_datetime=F('requested_admission_datetime'),
        delivery_datetime=F('estimated_due_date'),
        fully_dilation_datetime=ExpressionWrapper(
            F('estimated_due_date') - timedelta(days=1), 
            output_field=DateTimeField()
        ),
        source=Value('booking', output_field=CharField())
    ).filter(
        status="ACCEPTED", 
        admission_datetime__date=current_date
    ).values(
        'first_name', 'last_name', 'delivery_type', 'date_of_birth',
        'admission_datetime', 'delivery_datetime', 'fully_dilation_datetime', 'source'
    )

    # 构建符合条件的patient数据集
    patient_data = PatientDetails.objects.annotate(
        admission_datetime=F('created_at'),
        delivery_datetime=F('estimated_due_date'),
        fully_dilation_datetime=F('patient_fully_dilated'),
        source=Value('patient', output_field=CharField())
    ).filter(
        status="ACTIVE",
        admission_datetime__date=current_date
    ).values(
        'first_name', 'last_name', 'delivery_type', 'date_of_birth',
        'admission_datetime', 'delivery_datetime', 'fully_dilation_datetime', 'source'
    )

    # 合并两个数据集(all=True保留所有记录,包括字段重复的)
    combined = booking_data.union(patient_data, all=True)

    # 给每组相同标识的记录添加排名,source='patient'排第一
    combined_with_rank = combined.annotate(
        row_num=Window(
            expression=RowNumber(),
            partition_by=['first_name', 'last_name', 'date_of_birth'],
            order_by=F('source').asc()  # 'patient'字典序比'booking'靠前,因此排名为1
        )
    )

    # 筛选每组第一条记录,即优先保留source=patient的记录
    result = combined_with_rank.filter(row_num=1).order_by('first_name', 'last_name', 'date_of_birth')

    return result

方案二:先保留patient记录,再补充无重复的booking记录

逻辑更直观:先获取所有符合条件的patient记录,再筛选出那些不在patient记录中的booking记录,最后合并两者。

修改后的代码:

from django.db.models import F, Q, Value
from django.db.models import ExpressionWrapper, DateTimeField
from datetime import datetime, timedelta

def get_queryset(self):
    current_date = datetime.today()

    # 获取符合条件的patient数据集
    patient_data = PatientDetails.objects.annotate(
        admission_datetime=F('created_at'),
        delivery_datetime=F('estimated_due_date'),
        fully_dilation_datetime=F('patient_fully_dilated'),
        source=Value('patient', output_field=CharField())
    ).filter(
        status="ACTIVE",
        admission_datetime__date=current_date
    ).values(
        'first_name', 'last_name', 'delivery_type', 'date_of_birth',
        'admission_datetime', 'delivery_datetime', 'fully_dilation_datetime', 'source'
    )

    # 提取patient的三个标识字段组合,用于排除booking中的重复记录
    patient_identifiers = patient_data.values_list('first_name', 'last_name', 'date_of_birth')

    # 获取无重复的booking数据集(排除已经在patient中存在的记录)
    booking_data = PatientBooking.objects.annotate(
        admission_datetime=F('requested_admission_datetime'),
        delivery_datetime=F('estimated_due_date'),
        fully_dilation_datetime=ExpressionWrapper(
            F('estimated_due_date') - timedelta(days=1), 
            output_field=DateTimeField()
        ),
        source=Value('booking', output_field=CharField())
    ).filter(
        status="ACCEPTED", 
        admission_datetime__date=current_date,
        ~Q((F('first_name'), F('last_name'), F('date_of_birth'))__in=patient_identifiers)
    ).values(
        'first_name', 'last_name', 'delivery_type', 'date_of_birth',
        'admission_datetime', 'delivery_datetime', 'fully_dilation_datetime', 'source'
    )

    # 合并patient和无重复的booking数据
    combined_queryset = patient_data.union(booking_data).order_by('first_name', 'last_name', 'date_of_birth')

    return combined_queryset

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 20:14:50