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因为它与结果集无关)
这让我困惑了一段时间。
我通过 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因为它与结果集无关)