Django использует подзапрос для аннотации подсчета различных значений поля внешнего ключа

Я пытаюсь аннотировать количество отдельных продуктов, но пока не удалось. Я получаю ошибку следующего вида:

ProgrammingError: subquery must return only one column
LINE 1: ..._id", "shop_selectedproduct"."create_date", COUNT((SELECT U0...

У меня есть следующие модели:

class ShopItem(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    store = models.ForeignKey(to=Store, on_delete=models.CASCADE)
    name = models.CharField(max_length=250)

class SelectedProduct(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="products", null=True, blank=True)
    product = models.ForeignKey(ShopItem, null=True, blank=True, on_delete=models.SET_NULL)

class Order(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    store = models.ForeignKey(Store, on_delete=models.CASCADE, related_name="orders")
    status = models.PositiveIntegerField(choices=OrderStatus.choices, default=OrderStatus.CART)
    number = models.CharField(max_length=36, blank=True, null=True)

моя попытка подсчитать отдельные товары магазина, например p1, p2, p3, p4 and p5 где количество p1 должно быть 3, потому что он выбран три раза, в то время как другие могут иметь количество только один, так как мы выбрали только один из каждого товара.

Моя попытка на данный момент заключается в следующем:

# order = 1
SelectedProduct.objects.filter(order=order).annotate(quantity=Count(Subquery(ShopItem.objects.filter(id=OuterRef("product")))))

Могу ли я подсчитать, сколько раз был выбран продукт, и указать это в selected_product, например:

p1.quanity
> 3
p2.quanity
> 3
p3.quanity
> 3
p4.quanity
> 1
p5.quanity
> 1

Буду признателен за любые соображения.

Предполагая, что вы хотите делать это на основе каждого заказа, вы могли бы сделать запрос из самого экземпляра заказа и аннотировать связанный менеджер?

# Assuming the instance is self
self.products.all().annotate(
    quantity=Count(
        'product', distinct=True
    ),
)

попробуйте это:

SelectedProduct.objects.filter(
   order=order
).values(
   'product'
).annotate(
   quantity=Count('product')
).order_by('product') 

Кроме того, вы можете подсчитать данные для всех выбранных продуктов:

SelectedProduct.objects.values(
   'product'
).annotate(
   quantity=Count('product')
).order_by('product') 

если вы хотите подсчитать все выбранные товары для всех товаров

ShopItem.objects.annotate(
   quantity=Count('selectedproduct')
)

Если вы хотите получить коллекцию order экземпляров ShopItem в этом заказе с указанием их количества в заказе, вы можете сделать запрос к таблице ShopItem.

products = (ShopItem.objects.filter(selectedproduct__order=order)
                            .annotate(quantity=Count('selectedproduct')))

Кажется, я нашел решение для этого, если кому-то нужно что-то подобное:

quantity = ShopItem.objects.filter(
    id=OuterRef("product_id"),
    selectedproduct__order=order).annotate(
            quantity=Count('selectedproduct')
).values_list("quantity", flat=True)

order.products.all().select_related("product").annotate(quantity=Subquery(quantity))

Подзапрос quantity принимает id товара (shopitem id) с помощью OuterRef и подсчитывает, сколько выбранных товаров order. мы используем .values_list("quantity", flat=True) для получения плоского значения (поскольку возвращаемые результаты являются единичными значениями) аннотированного количества. Например, подзапрос вернет значение 3, которое мы используем непосредственно в основном запросе.

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