如何用多个值注释 Django 查询集

问题描述 投票:0回答:1

我有一个每天刷一些网店的应用,我记录每一个

Price
Item

我的模型的简化版本如下所示:

class Item(models.Model):
    name = models.CharField(max_length=512, blank=True)


class Price(models.Model):
    item = models.ForeignKey("Item", on_delete=models.CASCADE)
    timestamp = models.DateTimeField(blank=True)
    per_item = models.FloatField(blank=True)

我的桌子看起来像这样:

# Item

id  | name
-----------
1i  | item-1
2i  | item-2
3i  | item-3

# Price

id  | item   | timestamp   | per_item
--------------------------------------
1p  | i1     | 2023-04-10  | 10.0
2p  | i2     | 2023-04-10  | 2.0
3p  | i3     | 2023-04-10  | 14.5

4p  | i1     | 2023-04-11  | 11.0
5p  | i3     | 2023-04-11  | 12.5

6p  | i2     | 2023-04-12  | 2.0
7p  | i3     | 2023-04-12  | 14.5

8p  | i1     | 2023-04-13  | 12.0
9p  | i2     | 2023-04-13  | 3.5
10p | i3     | 2023-04-13  | 15.0

请注意,并非所有项目每天都存在,其中一些可能今天存在,明天丢失,后天又出现,依此类推。

我想做什么

我正在尝试编写一个查询,在给定的一天,它将为我提供当天存在的每个项目的last 2 Prices

所以,如果我查看最后一天 (2023-04-13),我会得到这样的结果:

[
{'id': '1i', 'latest_prices': [{'price__id': '8p', 'price__per_item': 12.0}, {'price__id': '4p', 'price__per_item': 11.0}]
{'id': '2i', 'latest_prices': [{'price__id': '9p', 'price__per_item': 3.5}, {'price__id': '6p', 'price__per_item': 2.0}]
{'id': '3i', 'latest_prices': [{'price__id': '10p', 'price__per_item': 15.0}, {'price__id': '7p', 'price__per_item': 14.5}]
]

我每天有大约 25k 件物品,所以我不想:

  • 必须为每个
    Price
    去数据库检索它的
    per_item
    字段
  • 必须遍历每一天的每一个
    Item
    ,以检索它的最后2个
    Price
    s

我试过的

  1. 我试过使用
    annotate
    Subquery
    ,但我得到了
    django.db.utils.ProgrammingError: more than one row returned by a subquery used as an expression
  2. 然后我发现在 postgres 上我可以使用一个
    ArraySubquery
    但我只能返回多个
    price__id
    ,而不是多个字段
  3. 然后我仔细查看了
    ArraySubquery
    链接中的示例,我注意到了
    JSONObject
    - 但是我可能以错误的方式持有它并且我总是在
    latest_prices
    注释中得到一个项目。

怀疑 解决方案是

JSONObject
方法,但不知何故,目前我的想法还遥不可及,因此非常感谢任何朝着正确方向的引导:)

python django postgresql django-orm
1个回答
0
投票

对,所以答案确实是

ArraySubquery
JSONObject
的组合,看起来像这样:

subquery = (
    Price.objects.filter(item__id=OuterRef("id"))
        .filter(timestamp__lt=end_of_today)
        .order_by("-retrieval__timestamp")
        .values(json=JSONObject(price_id="id", per_item="per_item"))[:2]
    )

items_with_prices = (
    Item.objects.filter(id__in=item_ids)
        .annotate(latest_prices=ArraySubquery(subquery))
        .values("id", "latest_prices")
)

它对我不起作用的原因是因为我忘记了一个调试语句,它限制了我可以查看多少天🤦u200d♂️

© www.soinside.com 2019 - 2024. All rights reserved.