我有一个每天刷一些网店的应用,我记录每一个
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
sannotate
和Subquery
,但我得到了django.db.utils.ProgrammingError: more than one row returned by a subquery used as an expression
ArraySubquery
但我只能返回多个 price__id
,而不是多个字段ArraySubquery
链接中的示例,我注意到了 JSONObject
- 但是我可能以错误的方式持有它并且我总是在 latest_prices
注释中得到一个项目。我 怀疑 解决方案是
JSONObject
方法,但不知何故,目前我的想法还遥不可及,因此非常感谢任何朝着正确方向的引导:)
对,所以答案确实是
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♂️