Django ManyToMany 链式查询集未给出 ANDed 结果

Django ManyToMany chained queryset not giving ANDed result

这让我困惑了一段时间。

我通过 table 建立了多对多关系,如图所示:

class Coupon(TimeStampedUUIDModel):
    venue = models.ManyToManyField('venues.Venue', through='VenueCouponConfig', related_name='coupons')


    class Meta:
        verbose_name = _('Coupon')
        verbose_name_plural = _('Coupons')
        ordering = ('-created_at', )


class VenueCouponConfig(UUIDModel):
    venue = models.ForeignKey(
        'venues.Venue', null=True, blank=True, on_delete=models.SET_NULL
    )
    coupon = models.ForeignKey(
        'Coupon', null=True, blank=True, on_delete=models.SET_NULL
    )
    is_activated = models.BooleanField(_('Activate Coupon'), null=False, blank=True)

本质上,我想让优惠券与多个场所相关联,所以我通过 table 创建每个场所都有 is_activated 标志,它描述某个场所是否激活了某些优惠券。

现在,我有这个 API,其中列出了特定地点的所有优惠券,例如:

...
venue = get_object_or_404(self.queryset, pk=pk)
qs = Coupon.objects.all()
qs = qs.prefetch_related(Prefetch('venuecouponconfig_set'))
qs = qs.filter(venue=venue)
qs = qs.filter(venuecouponconfig__is_activated=True)
qs = qs.order_by('created_at')
...

现在评价这个API多次送我一些优惠券

但是如果我像这样附加过滤器:

...
venue = get_object_or_404(self.queryset, pk=pk)
qs = Coupon.objects.all()
qs = qs.prefetch_related(Prefetch('venuecouponconfig_set'))
qs = qs.filter(venue=venue, venuecouponconfig__is_activated=True)
qs = qs.order_by('created_at')
...

这给了我正确的结果,优惠券出现了一次。

链式过滤器与 ManyToManyField 的工作方式是否不同?除非在单个过滤器查询中提到了这两个东西,否则它不适合做 AND 吗?

我是不是遗漏了什么?

如果需要更多信息,请告诉我。

通过进行单独的 filter() 调用,您可以创建独立的过滤器。

qs = Coupon.objects.all()
qs = qs.filter(venue=venue)
qs = qs.filter(venuecouponconfig__is_activated=True)

→ 过滤所有符合场地的优惠券。
→ 并过滤所有与 is_activated 有关系的所有优惠券。

qs = Coupon.objects.all()
qs = qs.filter(venue=venue, venuecouponconfig__is_activated=True)

→ 过滤所有与场地匹配且特定场地的优惠券 is_activated 为真。

如您所见,在这两种情况下您都在执行逻辑与,但应用的点不同。

在技术方面,第一个请求创建两个单独的 JOIN 并将每个过滤器应用于其中一个,而第二个请求仅创建一个 JOIN 并对其应用两个过滤器。

根据经验,每个 filter() 调用都会创建一组不同的 JOIN,后续调用不会重复使用这些 JOIN。

(我去掉了prefetch_related因为它与结果集无关)