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,提问作者Кирилл Бондарь
相关产品推荐
相关产品推荐

