如何在prefetch_related中排除空的查询集实例?

1 投票
1 回答
28 浏览
提问于 2025-04-12 14:06

我有两个模型,分别是 ProjectContract
它们之间是一对多的关系。

class Contract(models.Model):
    project = models.ForeignKey(Project)

我获取了一些 Project 实例,并且这些实例有相关的 Contract 实例。

projects = Project.objects.filter(active=True).prefetch_related(
        Prefetch('contract_set', queryset=Contract.objects.filter(**filters), to_attr='contracts')
    )

但现在我需要通过把 Contract 的唯一标识符放到 filters 中,来获取与特定 Contract 实例完全匹配的 Project 实例。

filters: Dict = {
  'guid': some_guid,
}

但是我得到的却是所有的 Project 实例,其中只有一个实例的 contracts 属性不为空,而其他的 Project 实例的 contracts 属性都是空的。

我发现了一些相关的问题:
如何排除空的预取相关字段的行
在 Django 中过滤空的预取相关字段
但这些对我没有帮助。

我尝试了以下几种方法:
a) 使用 OuterRefExist

projects = Project.objects.filter(**filters_projects).annotate(
    has_contracts=Exists(
        Contract.objects.filter(project_id=OuterRef('pk'), **filters_contracts)
    )
)

projects = projects.filter(has_contracts=True)

b) 使用 annotate

projects = (
        Project.objects.filter(**filters_projects)
        .annotate(num_contracts=Count('contract'))
        .exclude(num_contracts=0)
        .prefetch_related(
            Prefetch('contract_set', queryset=Contract.objects.filter(**filters_contracts), to_attr='contracts')
        )
    )

这些方法对我都没用...

我该如何实现我想要的功能呢?

1 个回答

2

你需要在 Prefetch 对象和 Project 中都进行筛选,也就是说:

projects = (
    Project.objects.filter(
        Exists(
            Contract.objects.filter(
                project_id=OuterRef('pk'), **filters_contracts
            )
        ),
        **filters_projects
    )
    .prefetch_related(
        Prefetch(
            'contract_set',
            queryset=Contract.objects.filter(**filters_contracts),
            to_attr='contracts',
        )
    )
    .distinct()
)

所以你要用 **filters_contracts 两次:第一次是为了只获取这些 Project,第二次是为了处理预先获取的对象。

撰写回答