Как выполнить __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
)
Все фрагменты кода предполагают, что топпинг может произойти только один раз.