Django:在请求值时从查询集注释字段中删除小数前缀

Posted

技术标签:

【中文标题】Django:在请求值时从查询集注释字段中删除小数前缀【英文标题】:Django: remove Decimal prefix from queryset annotated field, when requesting values 【发布时间】:2020-05-04 06:24:57 【问题描述】:

简而言之,我有这个查询集:

monthly_revenue = list(Booking.objects.annotate(month=Month('created_at'))
                           .values('month')
                           .annotate(total=Sum('price'))
                           .order_by('month'))

这是它返回的内容:

['month': 11, 'total': Decimal('4550.00')]

结果将转到 js 脚本以显示图形,我需要删除 Decimal() 前缀。 任何帮助或提示表示赞赏。

【问题讨论】:

【参考方案1】:

如果您只想删除“十进制”前缀,您可以在注释中定义一个特定的输出字段:

monthly_revenue = list(Booking.objects.annotate(month=Month('created_at'))
                           .values('month')
                           .annotate(total=Sum('price', output_field=FloatField()))
                           .order_by('month'))

【讨论】:

【参考方案2】:

由于您使用的是Django,因此您可以使用DjangoJSONEncoder,如下所示:

from django.core.serializers.json import DjangoJSONEncoder

my_dict = ['month': 11, 'total': Decimal('4550.00')]
json_result = json.dumps(my_dict, cls=DjangoJSONEncoder)

但是,请记住,DjangoJSONEncoder 会将十进制转换为字符串,因此结果将是:

["month": 11, "total": "4550.00"]

如果您导航到 DjangoJSONEncoder 源代码,您会发现:

elif isinstance(o, (decimal.Decimal, uuid.UUID, Promise)):
    return str(o)

【讨论】:

我已经为模型序列化扩展了该编码器,现在我只是将其调整为将十进制转换为浮点数,我将分享解决方案。【参考方案3】:

你可以把它转换成浮点型,像这样

monthly_revenue = list(Booking.objects.annotate(month=Month('created_at'))
                       .values('month')
                       .annotate(total=float(Sum('price')))
                       .order_by('month'))

【讨论】:

我早些时候尝试过,它不起作用它抛出:Traceback (most recent call last): File "<input>", line 3, in <module> TypeError: float() argument must be a string or a number, not 'Sum' 您可以在调用 Sum 函数之前将其转换为浮点数 不能在查询float() argument must be a string or a number, not 'F'中使用float【参考方案4】:

我终于找到了两个对我有用的解决方案: 感谢@Ramy 的回答,我覆盖了DjangoJSONEncoder,以支持将小数转换为浮点数:

import decimal
import uuid
from django.utils.functional import Promise
from django.core.serializers.json import DjangoJSONEncoder

class JsonDecimalToFloatEncoder(DjangoJSONEncoder):
def default(self, o):
    if isinstance(o, (decimal.Decimal, uuid.UUID, Promise)):
        return float(o)
    return super().default(o)

我按照他所说的那样使用它:

monthly_revenue = list(Booking.objects.annotate(month=Month('created_at'))
                               .values('month')
                               .annotate(total=Sum('price'))
                               .order_by('month'))
json_result = json.dumps(monthly_revenue,cls=ExtendedEncoder)
json_result
'["month": 11, "total": 4550.0]'

这个解决方案需要在服务器端做更多的工作,所以我选择了@rossi 的第二个解决方案,它在数据库而不是服务器上做更多的工作,这就是 django 的文档所建议的。

from django.db.models import FloatField
from django.db.models.functions import Cast

list(Booking.objects.annotate(month=Month('created_at'))
                              .values('month')                                
                              .annotate(total_as_f=Cast('total', 
                               output_field=FloatField()))
                              .order_by('month'))

['month': 11, 'total': Decimal('4550.00'), 'total_as_f': 4550.0]

希望这对未来的任何人都有帮助。

【讨论】:

以上是关于Django:在请求值时从查询集注释字段中删除小数前缀的主要内容,如果未能解决你的问题,请参考以下文章

与 django 查询集注释中的先前对象的区别

为查询集注释排名字段的正确方法

TFS 签入时,提示“变更集注释策略 中的内部错误……”

将更新签入到 TFS 后编辑变更集注释

(转)解决swagger跨项目或跨程序集注释不显示问题

为啥在 django 中进行大型查询(或一系列查询)后内存没有释放到系统?