如何在Django中通过数据库端筛选所有书籍已读的Person?
问题:筛选所有拥有的书籍全部已读的Person对象
现有Django模型定义如下(注:原代码中Integerfield应为IntegerField,已修正):
class Person(models.Model): id = IntegerField(primary_key=True) class Book(models.Model): id = IntegerField(primary_key=True) owner = ForeignKey("Person") is_read = BooleanField()
曾尝试用Person.objects.filter(book__is_read=True)筛选,但该语句仅能选出至少有一本已读书籍的用户,无法实现「所有关联书籍都已读」的全量满足逻辑。
目前使用DRF序列化器处理数据,序列化器代码存在模型名笔误(Auftraege应为Book,Probanden应为Person),修正后如下:
class SerializerBook(ModelSerializer): class Meta: model = Book fields = ["id","owner","is_read"] class SerializerPerson(ModelSerializer): book = SerializerBook(read_only=True, many=True) class Meta: model = Person fields = ["id","book"]
原本计划在Python层循环过滤序列化后的数据,但希望直接通过数据库查询完成筛选,询问可行方法。
解决方案
直接在数据库层面筛选能大幅提升性能,以下是几种可行方法:
方法一:排除法(最简单直观)
排除所有存在未读书籍的用户,剩余的就是所有书籍都已读的用户:
Person.objects.exclude(book__is_read=False)
如果需要排除没有任何书籍的用户,可额外添加过滤条件:
Person.objects.exclude(book__is_read=False).filter(book__isnull=False)
方法二:聚合统计法(精准控制逻辑)
通过聚合函数统计每个用户的未读书籍数量,筛选数量为0的用户:
from django.db.models import Count, Q Person.objects.annotate( unread_books_count=Count('book', filter=Q(book__is_read=False)) ).filter(unread_books_count=0)
同样,若要排除无书籍用户,可追加filter(book__isnull=False)。
方法三:子查询排除法(大数据量下更高效)
先通过子查询找出所有拥有未读书籍的用户ID,再在主查询中排除这些ID:
from django.db.models import Subquery, OuterRef # 子查询:获取所有有未读书籍的用户ID unread_person_ids = Book.objects.filter( owner=OuterRef('pk'), is_read=False ).values('owner') # 主查询:排除上述用户 Person.objects.exclude(id__in=Subquery(unread_person_ids))
这种方式能更好地利用数据库索引,数据量较大时性能更优。
内容的提问来源于stack exchange,提问作者Zergoholic
相关产品推荐
相关产品推荐

