Использование подзапроса для получения 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))
)
Вернуться на верх