Как выполнить __all_in запрос в Django?

У меня есть три модели

class Pizza(models.Model):
    toppings = models.ManyToManyField(Topping)

class Topping(models.Model):
    name = models.CharField(max_length=50)

class Order(models.Model):
    must_have_toppings = models.ManyToManyField(Topping)

Я хочу найти все заказы, которые соответствуют определенной пицце. Для этого я хотел бы сделать что-то вроде

orders = Order.objects.filter(must_have_toppings__all_in=my_pizza.toppings)

Что я пробовал:

orders = Order.objects.filter(must_have_toppings__in=my_pizza.toppings) не работает, потому что будут возвращены заказы с одним из начинок пиццы в их must_have.

И:

orders = Orders.objects
for topping in my_pizza.toppings.all():
    orders = orders.filter(must_have_toppings=topping)

не работает, потому что он вернет заказы, в которых есть все начинки из пиццы, даже если некоторые из must_have_toppings отсутствуют. Например, пицца с помидорами и грибами вернет заказ, в котором нужны помидоры, перец и грибы.

Как я могу искать заказы, в которых обязательные_элементы есть ВСЕ в объекте Pizza?

(я использую MySql)

Если вы хотите найти заказы, где все Topping в заказе принадлежат my_pizza, мы можем отфильтровать с помощью:

from django.db.models import F, Q

toppings = my_pizza.toppings.all()

Order.objects.annotate(
    ntoppings=Count(
        'must_have_toppings',
        filter=Q(must_have_toppings__in=toppings)
    ),
    total=Count('must_have_toppings')
).filter(
    ntoppings=F('total')
)

Сначала мы получаем все топпинги из my_pizza, а затем проверяем, совпадает ли количество топпингов i с количеством топпингов, принадлежащих my_pizza, если да, то мы знаем, что все топпинги заказа являются членами этой пиццы.

Мы также можем сделать наоборот и проверить, все ли начинки my_pizza принадлежат Order:

from django.db.models import Count, Q

toppings = my_pizza.toppings.all()
ntoppings = len(toppings)

Order.objects.annotate(
    ntoppings=Count(
        'must_have_toppings',
        filter=Q(must_have_toppings__in=toppings)
    )
).filter(
    ntoppings=ntoppings
)

Если в заказе Order больше начинок, чем в пицце, то этот заказ все равно будет отображаться. Так, если пицца имеет в качестве начинки сыр и ананас, то заказ с сыром, салями и ананасом будет соответствовать.

Для точного совпадения, когда заказ и my_pizza имеют одинаковые начинки, мы можем работать с:

from django.db.models import Count, Q

toppings = my_pizza.toppings.all()
ntoppings = len(toppings)

Order.objects.annotate(
    ntoppings=Count(
        'must_have_toppings',
        filter=Q(must_have_toppings__in=toppings)
    ),
    total=Count('must_have_toppings')
).filter(
    ntoppings=ntoppings,
    total=ntoppings
)

Все фрагменты кода предполагают, что топпинг может произойти только один раз.

Вернуться на верх