Django ManyToMany с цепочкой запросов не дает ANDed результат - PullRequest
0 голосов
/ 04 июня 2018

Это оставляет меня на некоторое время сбитым с толку.

У меня установлены отношения ManyToMany с помощью таблицы, как показано ниже:

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, если обе вещи не были упомянуты в одном запросе фильтра?

Я что-то здесь упускаю?

Дайте мне знать, нужна ли какая-либо дополнительная информация.

1 Ответ

0 голосов
/ 04 июня 2018

Делая отдельные вызовы filter(), вы создаете независимые фильтры.

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

→ фильтруйте все купоны, соответствующие месту встречи.
→ И фильтруйте все купоны, которые имеют одно из их отношений с is_activated true.

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

→ отфильтровать все купоны, которые соответствуют месту проведения И что определенное место имеет is_activated true.

Как вы можете видеть, в обоих случаяхвы выполняете логическое И, но точка, в которой он применяется, отличается.

С технической стороны первый запрос создает два отдельных соединения и применяет каждый фильтр к одному, а второй - только один.JOIN и применяет к нему оба фильтра.

Как правило, каждый вызов filter() создает отдельный набор JOIN, который не используется последующими вызовами.

(Я удалил prefetch_related, так как он не имеет отношения к набору результатов)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...