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

如何利用Assignment自定义管理器高效筛选拥有已批准作业的用户?

问题描述

现有一组用户和用户提交的作业,对应的Django模型定义如下:

class User(models.Model):
    name = models.CharField()

class Assignment(models.Model):
    user = models.ForeignKey(
        "User",
        related_name="assignments"
    )
    status = models.CharField()

    approved = AssignmentActiveManager()
    rejected = AssignmentRejectedManager()
    ...

由于作业状态的判断逻辑复杂,需要封装在模型内部,因此为Assignment创建了自定义管理器,示例如下:

class AssignmentActiveManager(models.Manager):
    def get_queryset(self):
        return Assignment.objects.filter(status__in=["Approved", "Accepted"])

现在希望通过Assignment.approved管理器筛选出所有拥有已批准作业的用户,避免重复编写筛选逻辑。目前尝试的代码:

User.objects.filter(assignments__in=Assignment.approved.all()).all()

但该查询会生成WHERE status IN (SELECT ...)子查询,效率低于直接编写筛选条件的查询:

User.objects.filter(assignments__status__in=["Approved", "Accepted"]).all()

后者会执行INNER JOIN并通过WHERE user.assignment_id IN (Approved, Accepted)筛选。

问题:能否通过Assignment自定义管理器高效筛选出符合条件的用户?

解决方案

当然可以,以下几种方法都能利用自定义管理器的逻辑,同时生成高效的JOIN查询而非子查询:

1. 封装筛选条件为可复用的Q对象

修改AssignmentActiveManager,将筛选逻辑封装为Q对象,既供管理器自身使用,也能在关联查询中直接复用:

class AssignmentActiveManager(models.Manager):
    # 把状态筛选逻辑封装成Q对象
    approved_status_filter = models.Q(status__in=["Approved", "Accepted"])

    def get_queryset(self):
        return Assignment.objects.filter(self.approved_status_filter)

查询用户时直接调用这个Q对象:

User.objects.filter(assignments__in=Assignment.approved_status_filter).all()

这种方式生成的SQL和直接写assignments__status__in=["Approved", "Accepted"]完全一致,会使用INNER JOIN,避免子查询的性能损耗。

2. 为User模型添加关联逻辑的自定义管理器

给User模型创建专属管理器,内部复用Assignment.approved的逻辑:

class UserManager(models.Manager):
    def with_approved_assignments(self):
        # 复用Assignment.approved的查询条件,生成高效JOIN
        return self.filter(assignments__in=Assignment.approved.get_queryset().query)

在User模型中注册该管理器:

class User(models.Model):
    name = models.CharField()
    objects = UserManager()

调用时只需:

User.objects.with_approved_assignments().all()

这种方式完全封装了查询逻辑,外部调用简洁,同时保证查询效率。

3. 利用反向关联的查询集推导

如果不想修改现有管理器,也可以通过Assignment.approved的查询集直接提取用户ID,再去重查询:

User.objects.filter(pk__in=Assignment.approved.values_list('user_id', flat=True)).distinct()

注:这种方式可能生成子查询,但多数现代数据库会对这类子查询做优化,性能差距不大;若追求严格的JOIN查询,优先选择前两种方法。

内容的提问来源于stack exchange,提问作者Enrico Borba

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 13:47:36