Использование подзапроса для получения COUNT из другой таблицы в Django
У меня есть эти модели:
class IceCream(models.Model):
name = models.CharField(max_length=255)
class Topping(models.Model):
name = models.CharField(max_length=255)
ice_cream = models.ForeignKey(IceCream, on_delete=models.CASCADE, related_name="toppings")
is_active = models.BooleanField(db_index=True)
Я хотел бы выполнить следующий запрос, чтобы получить список всех мороженых вместе с подсчетом общего количества доступных начинок, что в SQL будет выглядеть следующим образом:
SELECT ice_cream.*,
(SELECT COUNT(id) FROM topping WHERE topping.ice_cream = ice_cream.id AND topping.is_active = True) AS total_toppings
FROM ice_cream
Это упрощенная версия, в ней больше параметров, но если я смогу заставить это работать, то все должно получиться. Могу ли я сделать это в Django, используя выражения Subquery? Мне не удалось заставить это работать. Или необработанные запросы - мой единственный выход из-за COUNT, который я хочу включить в подзапрос?
Для этого можно использовать annotate
, который поддерживает агрегационные функции типа Count
. Вы также можете добавить фильтры к функциям агрегации, предоставив объект Q
.
# ...
from django.db.models import Count, Q
# ...
qs = IceCream.objects.all().annotate(
active_toppings_count=Count("toppings", filter=Q(toppings__is_active=True))
)
for ice_cream in qs:
print(ice_cream.name, ice_cream.active_toppings_count)
Пакет django-sql-utils упрощает эту задачу, pip install django-sql-utils
а затем
from sql_util.utils import SubqueryCount
queryset = IceCream.objects.all().annotate(
total_toppings=SubqueryCount('toppings', filter=Q(is_active=True))
)