django ORM 将相关 table 上的两个条件转换为两个单独的 JOIN

django ORM turns two conditions on related table into two separate JOINs

我有这样的情况,我需要从相关 table.

中过滤两个属性
class Item(models.Model):
     vouchers = models.ManyToManyField()


class Voucher(models.Model):
     is_active = models.BooleanField()
     status = models.PositiveIntegerField()

当我这样查询 ORM 时:

Item.objects.exclude(
    vouchers__is_active=False, 
    vouchers__status__in=[1, 2])      

创建的查询如下所示:

SELECT *
FROM `item`
WHERE NOT (`item`.`id` IN (
   SELECT U1.`item_id`
   FROM `itemvouchers` U1
   INNER JOIN `voucher` U2 ON (U1.`voucher_id` = U2.`id`)
   WHERE U2.`is_active` = FALSE) 
 AND 
`item`.`id` IN (
   SELECT U1.`item_id`
   FROM `itemvouchers` U1
   INNER JOIN `voucher` U2 ON (U1.`voucher_id` = U2.`id`)
   WHERE U2.`status` IN (1, 2))
)

我想排除既不活动又状态为 1 或 2 的代金券。

查询所做的是创建两个单独的联接。这起初是不必要的,而且对性能不利。其次就是错了。

案例:

voucher_a = Voucher.objects.create(status=3, is_active=True)
voucher_b = Voucher.objects.create(status=1, is_active=False)

如果我有一个与 voucher_avoucher_b 相关的项目,则找不到它,因为它在 JOIN 1 中但不在 JOIN 2 中。

它看起来像是 django 中的一个错误,但我无法在网上找到任何对这个主题有用的东西。

我们在 django==2.1.1 上并尝试将 exclude 切换为 filter 或使用 Q 表达式。到目前为止没有任何效果。

谢谢

罗恩

您的设置是一种 m2m 关系,您想要排除具有至少一个 m2m 关系且此 AND 条件组合为真的任何单个对象。

M2M 关系在 filter/exclude 查询集方面很特殊,请参阅 https://docs.djangoproject.com/en/2.1/topics/db/queries/#spanning-multi-valued-relationships

另请注意该文档:

The behavior of filter() for queries that span multi-value relationships, as described above, is not implemented equivalently for exclude(). Instead, the conditions in a single exclude() call will not necessarily refer to the same item.

文档中提供的解决方案如下:

Blog.objects.exclude(
    entry__in=Entry.objects.filter(
        headline__contains='Lennon',
        pub_date__year=2008,
    ),
)