[英]Complex Django query involving an ArrayField & coefficients
一方面,讓我們考慮這個 Django model:
from django.db import models
from uuid import UUID
class Entry(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
value = models.DecimalField(decimal_places=12, max_digits=22)
items = ArrayField(base_field=models.UUIDField(null=False, blank=False), default=list)
另一方面,假設我們有這本字典:
coefficients = {item1_uuid: item1_coef, item2_uuid: item2_coef, ... }
Entry.value
旨在根據coefficients
在Entry.items
之間分配。
使用 Django ORM,什么是最有效的方式(在單個Entries
查詢中)獲取單個Item
的值的總和,給定系數?
例如,對於下面的item1
,我想得到168.5454...
,也就是說100 * 1 + 150 * (0.2 / (0.2 + 0.35)) + 70 * 0.2
。
條目編號 | 價值 | 項目 |
---|---|---|
uuid1 | 100 | [item1_uuid] |
uuid2 | 150 | [item1_uuid, item2_uuid] |
uuid3 | 70 | [item1_uuid, item2_uuid, item3_uuid] |
coefficients = { item1_uuid: Decimal("0.2"), item2_uuid: Decimal("0.35"), item3_uuid: Decimal("0.45") }
額外的問題:我如何調整我的模型來使這個查詢運行得更快? 我故意選擇使用ArrayField
並決定不使用ManyToManyField
,這是個壞主意嗎? 如何知道我可以在哪里為這個特定查詢添加db_index[es]
?
我正在使用 Python 3.10、Django 4.1。 和 Postgres 14。
我已經找到了解決我自己問題的方法,但我相信這里有人可以想出一種更高效、更清潔的方法。
這里的想法是在for
循環.alias()
方法(參見Django 文檔)和條件表達式與Case
和When
鏈接起來。
這會導致查詢過於復雜,但至少可以按預期工作:
def get_value_for_item(coefficients, item):
item_coef = coefficients.get(item.pk, Decimal(0))
if not item_coef:
return Decimal(0)
several = Q(items__len__gt=1)
queryset = (
Entry.objects
.filter(items__contains=[item.pk])
.alias(total=Case(When(several, then=Value(Decimal(0)))))
)
for k, v in coefficients.items():
has_k = Q(items__contains=[k])
queryset = queryset.alias(total=Case(
When(several & has_k, then=Value(v) + F("total")),
default="total",
)
)
return (
queryset.annotate(
coef_applied=Case(
When(several, then=Value(item_coef) / F("total") * F("value")),
default="value",
)
).aggregate(Sum("coef_applied", default=Decimal(0)))
)["coef_applied__sum"]
以我在問題中給出的示例和item1
為例,這個 function 的 output 是預期的Decimal(168.5454...)
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.