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

Django多表查询:筛选同名且拥有相同Appointment的重复Person

筛选Django中同名且共享职位的人员

场景与现有模型

我正在开发一个公共人物及其职位的数据库,现有Django模型如下:

class Person(models.Model):
    full_name = models.CharField()
    appointments = models.ManyToManyField('Appointment', through='PersonsNAppointments')

class Appointment(models.Model):
    position = models.CharField()
    persons = models.ManyToManyField('Person', through='PersonsNAppointments')

由于姓名不唯一,当名为John Donne的用户(id_1和id_2)被任命时,两人都会获得该职位。需要在管理面板中仅显示同名且拥有相同Appointment的人员,以便管理员手动处理重复项。目前已实现查找重复姓名的代码:

dup_names = Person.objects.values('full_name')\
                .annotate(name_count=Count('id'))\
                .filter(name_count__gt=1).values('full_name')

dup_objects = Person.objects.filter(full_name__in=dup_names)

进一步筛选共享相同Appointment的人员

方案一:使用Exists子查询(推荐,性能更优)

在Admin类的get_queryset方法中实现以下逻辑,通过子查询判断当前Person是否存在同名且共享至少一个职位的其他人员:

from django.db.models import Exists, OuterRef

class PersonAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        
        # 获取所有重复姓名的列表
        duplicate_names = Person.objects.values('full_name')\
            .annotate(name_count=Count('id'))\
            .filter(name_count__gt=1)\
            .values_list('full_name', flat=True)
        
        # 子查询:查找当前Person的同名且共享Appointment的其他Person
        same_name_shared_appt = Person.objects.filter(
            full_name=OuterRef('full_name'),
            id__ne=OuterRef('id'),  # 排除自身
            appointments__in=OuterRef('appointments')
        ).distinct()
        
        # 筛选符合条件的Person
        qs = qs.filter(
            full_name__in=duplicate_names,
            Exists(same_name_shared_appt)
        ).distinct()
        
        return qs

方案二:通过中间表统计关联关系

利用PersonsNAppointments中间表,统计同一姓名+同一职位下的人员数量,再提取对应的Person:

from django.db.models import Count, Q

class PersonAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        
        # 找到所有"同一姓名+同一职位"下人员数>1的记录
        duplicate_groups = PersonsNAppointments.objects.values(
            'person__full_name', 'appointment'
        ).annotate(
            person_count=Count('person', distinct=True)
        ).filter(person_count__gt=1)
        
        # 提取这些组对应的所有Person ID
        duplicate_person_ids = PersonsNAppointments.objects.filter(
            Q(person__full_name__in=duplicate_groups.values('person__full_name')) &
            Q(appointment__in=duplicate_groups.values('appointment'))
        ).values_list('person__id', flat=True).distinct()
        
        # 筛选出目标Person
        qs = qs.filter(id__in=duplicate_person_ids)
        
        return qs

说明

  • 方案一使用Exists子查询,数据库只需判断是否存在符合条件的关联记录,性能更高效,适合数据量较大的场景。
  • 方案二通过中间表直接统计重复组,逻辑更直观,适合对查询性能要求不高的小型数据库。

内容的提问来源于stack exchange,提问作者Кирилл Бондарь

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 09:54:33