Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
to be brief, i have this queryset:
monthly_revenue = list(Booking.objects.annotate(month=Month('created_at'))
.values('month')
.annotate(total=Sum('price'))
.order_by('month'))
this is what it is returning:
[{'month': 11, 'total': Decimal('4550.00')}]
the result is going to a js script to show a graph, and i need to remove the Decimal() prefix.
Any help or hint is appreciated.
If you just want to remove "Decimal" prefix you could just define a specific output field in your annotation:
monthly_revenue = list(Booking.objects.annotate(month=Month('created_at'))
.values('month')
.annotate(total=Sum('price', output_field=FloatField()))
.order_by('month'))
Since you're using Django you can use DjangoJSONEncoder as follows:
from django.core.serializers.json import DjangoJSONEncoder
my_dict = [{'month': 11, 'total': Decimal('4550.00')}]
json_result = json.dumps(my_dict, cls=DjangoJSONEncoder)
But, keep in mind that DjangoJSONEncoder turns decimal into strings so the result would be:
[{"month": 11, "total": "4550.00"}]
If you navigate to DjangoJSONEncoder source code you find this:
elif isinstance(o, (decimal.Decimal, uuid.UUID, Promise)):
return str(o)
–
you could convert it to a type float, like this
monthly_revenue = list(Booking.objects.annotate(month=Month('created_at'))
.values('month')
.annotate(total=float(Sum('price')))
.order_by('month'))
–
I finally found two solutions that worked for me:
Thanks to @Ramy's answer, I overidden the DjangoJSONEncoder, to support the conversion of a Decimal to a float:
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)
and I used it as he mentioned:
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}]'
this solution requires more work on the server side, so i opted for the second solution of @rossi which does more work on the database rather than the server, and that's what the django's documentation suggest.
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}]
hope this will help anyone in the futur.
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.