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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 14:18:16