簡體   English   中英

涉及 ArrayField 和系數的復雜 Django 查詢

[英]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旨在根據coefficientsEntry.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 文檔)和條件表達式CaseWhen鏈接起來。

這會導致查詢過於復雜,但至少可以按預期工作:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM