Как применить фильтр для каждой строки одного и того же набора запросов на Django

У меня есть модель Product, которая имеет отношение с самой собой, используя ключ "parent".

Таким образом, чтобы получить родительский продукт, я просто делаю product.parent, а если мне нужны дочерние продукты, я делаю product.product_set.all().

Я хочу сделать аннотацию на наборе родительских продуктов и суммировать запас каждого продукта с запасами его дочерних продуктов. Что-то похожее на это:

qs = Product.objects.filter(is_parent=True)
qs = qs.annotate(stock_sum=Sum(Product.objects.filter(parent_id=F('id')).values_list['stock']))

Возможно ли это? Приведет ли это к большому количеству запросов или django знает способ справиться с этим с помощью нескольких джойнов?

За исключением одной синтаксической ошибки (квадратные скобки вместо круглых после values_list) это должно работать так, как вы задумали.

Это вызывает только один запрос. Обратите внимание, что вы можете проверить SQL, который генерирует ORM, обратившись к свойству .query в наборе запросов (следующее было впоследствии прогнано через SQL pretty-printer):

>>> str(qs.query)
SELECT
    "yourapp_product"."id",
    "yourapp_product"."parent_id",
    "yourapp_product"."stock",
    SUM((SELECT U0."stock" FROM "yourapp_product" U0 WHERE U0."parent_id" = (U0."id"))) AS "stock_sum"
FROM
    "yourapp_product"
WHERE
    "yourapp_product"."is_parent"
GROUP BY
    "yourapp_product"."id",
    "yourapp_product"."parent_id",
    "yourapp_product"."stock";

Не уверен, как решить проблему с помощью Django ORM.

Но в SQL вы можете сделать это следующим образом:

SELECT
    p.id,
    (
    SELECT
        SUM(c.stock)
    FROM
        products_product c
    WHERE
        c.parent_id = p.id) + p.stock as stock_sum
FROM
    products_product p
WHERE
    p.parent_id is null

Вы можете выполнить sql-запрос в Django следующим образом:

products = Product.objects.raw("""
SELECT
    p.id,
    (
    SELECT
        SUM(c.stock)
    FROM
        products_product c
    WHERE
        c.parent_id = p.id) + p.stock as stock_sum
FROM
    products_product p
WHERE
    p.parent_id is null
""")

и затем использовать его, например, так [(p.id, p.stock_sum) for p in products]

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