Django djsonfield Decimal 在表达式中存储为字符串

Django djsonfield Decimal stored as string in expressions

我有一个包含一些财务数据的 JSONField。我不能将它存储为浮点数,因为我需要精度,所以我将它存储为字符串。

我的模型是这样的

class Finance(models.Model)
    bill_data = JSONField(
        verbose_name=_("Bill data"),
        default=dict,
        blank=True,
    )

那我存

bill_data = dict(paid=str(some_decimal_amount))

Finance.objects.create(bill_data=bill_data)

我尝试使用Cast将其转换回十进制,因为我需要使用表达式,

Finance.objects.all().annotate(paid=Cast('bill_data__paid', DecimalField()))

我收到一个错误

    return self.cursor.execute(sql, params)
django.db.utils.DataError: invalid input syntax for integer: "none"
LINE 1: ...der"."bill_data", ("myapp_finance"."bill_data")::numeric(No...

有谁知道如何在表达式中使用 JSONField 中的 str 或如何正确处理 JSONField 中的 Decimal

所以今天我为此苦苦挣扎。在帮助下,我们设法使其正常工作。

from django.db.models.functions import Cast
from django.db.models.fields.json import KeyTextTransform
from django.db.models import JSONField, DecimalField

from django.db import models


class Finance(models.Model):
    bill_data = JSONField(
        verbose_name="Bill data",
        default=dict,
        blank=True,
    )

好的,所以首先我假设 bill_data__paid 在任何地方都不是 Null。如果是,你应该先做一个:

Finance.objects.filter(bill_data__paid__isnull=False)

确保您没有投射任何 Null 字段。

好的,那我们试试你做的:

Finance.objects.annotate(
    paid=Cast("bill_data__paid", output_field=DecimalField()),
)

但是我们得到一个错误,大致如下:

invalid input syntax for type integer: "none" LINE 1: # ...("myapp_finance"."bill_data" -> 'paid') AS numeric(No......

好吧,那不是很好。那我们现在能做什么呢?好吧,也许我们需要指定小数字段的小数位数和最大位数,你说得对。

Finance.objects.annotate(
    paid=Cast("bill_data__paid", output_field=DecimalField(max_digits=6, decimal_places=2)),
)

但这行得通吗?可悲的是没有。我们现在收到错误

cannot cast jsonb string to type numeric

嗯,好的。不是很好,但至少这是一个不同的错误。问题是我们有一个 jsonb 字符串。让我们把它变成一个文本文本字符串(因为缺少更好的描述)

Finance.objects.annotate(
    paid=Cast(KeyTextTransform("paid", "bill_data"), output_field=DecimalField(max_digits=6, decimal_places=2))
)

现在,它将起作用。

因此我们将 jsonb 字符串转换为文本,然后将其转换为十进制(您必须指定小数位数和最大位数)。

结束:)