Django:请求值时从查询集注释字段中删除十进制前缀

Django: remove Decimal prefix from queryset annotated field, when requesting values

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

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() 前缀。 感谢任何帮助或提示。

你可以把它转换成 float 类型,像这样

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

由于您使用的是 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)

我终于找到了两个适合我的解决方案: 感谢 @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}]

希望这对以后的任何人都有帮助。

如果您只想删除 "Decimal" 前缀,您可以在注释中定义一个特定的输出字段:

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