【问题标题】:Django ManyToMany chained queryset not giving ANDed resultDjango ManyToMany链式查询集没有给出ANDed结果
【发布时间】:2018-11-14 02:05:57
【问题描述】:

这让我困惑了一段时间。

我通过如下表建立了多对多关系:

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)

基本上,我想让优惠券与多个场所相关联,因此我通过表格创建了每个场所的 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 吗?

我错过了什么吗?

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

【问题讨论】:

  • 它们可以像“乘数”一样发挥作用:对于每个“相关对象”,该对象都与相关对象成对被提及。

标签: django m2m


【解决方案1】:

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

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

→过滤所有与场地匹配的优惠券。
→ AND 过滤所有与is_activatedtrue 有关系的优惠券。

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

→ 过滤与场地匹配且特定场地具有is_activatedtrue 的所有优惠券。

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

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

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

(我删除了 prefetch_related 因为它与结果集无关)

【讨论】:

  • 哦,只有在 m2m 关系中使用直通表时才会发生这种情况。否则,我一直觉得链式查询集隐含地做了一个 AND 操作。对吗?
  • 正确。唯一的技巧是当你有一个“深度”的连接查询时,AND 操作适用于什么。如果在同一个filter() 中,它适用于整个连接路径,而如果在另一个filter() 中,它适用于根模型。
  • 谢谢,解决了这个问题。我已经接受并赞成你的回答:)
猜你喜欢
  • 2017-08-14
  • 2015-06-23
  • 2013-07-13
  • 1970-01-01
  • 2020-04-19
  • 1970-01-01
  • 2016-04-24
  • 2018-04-02
  • 2018-10-17
相关资源
最近更新 更多