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.
Related
I have a simple serializer with a date field (not ModelSerializer).
class MySerializer(Serializer):
some_date = DateField()
I'm trying to access the date object after deserialization.
slz = MySerializer(data={"some_date": "2020-05-03"})
# I surely have a better error handling in my actual code
assert slz.is_valid()
some_extracted_date = slz.data["some_date"]
I would like my variable some_extracted_date to be a datetime.date instance.
But the value in the MySerializer.data dict is a string.
Is there a way to get this datetime.date instance ?
You access data after validation using validated_data.
>>> from app.models import MySerializer
>>> slz = MySerializer(data={"some_date": "2020-05-03"})
>>> slz.is_valid(True)
True
>>> slz.data
{'some_date': '2020-05-03'}
>>> slz.validated_data
OrderedDict([('some_date', datetime.date(2020, 5, 3))])
>>> slz.validated_data['some_date']
datetime.date(2020, 5, 3)
I am running a django where i have some sort of conditions written based on the date.
My views.py file
from django.utils import timezone
def my_method(current_date=timezone.localdate()):
logger.info(f"Current date is obtained as {current_date}")
Whenever i load the page, my views.py internally calls this method and the date doesn't gets updated daily.
In settings.py
TIME_ZONE = 'PST8PDT'
USE_TZ = True
Irrespective of today's date, it always prints old dates and after few days the date gets updated to correct date and the same date gets continued for next 4-5 days.
What is that i am doing wrong here ?
P.S. I have also tried using python's native datetime.date.today() which resulted in same abnormal behavior.
As #Willem Van Onsem mentioned in this comment,
Because you pass a datetime object as default. Note that timezone.localdate is not re-evaluated in each call. It is evaluated once, and then reused.
So, just change your function as,
from django.utils import timezone
def my_method(current_date=None):
if current_date is None:
current_date = timezone.localdate()
print(current_date)
Example
In [1]: # suppose we have a function like this,
In [2]: from datetime import datetime
...:
...:
...: def foo(default=datetime.utcnow()):
...: return default
...:
In [3]: # and I am calling this 'foo' again and again and again
In [4]: # and expecting to return different value each time
In [5]: foo()
Out[5]: datetime.datetime(2020, 7, 27, 1, 48, 30, 525369)
In [6]: foo()
Out[6]: datetime.datetime(2020, 7, 27, 1, 48, 30, 525369)
In [8]: # see there is no evaluation happening.
In [9]: #
In [10]: # So, I have changed the function to ,
In [11]: def foo(default=None):
...: if default is None:
...: default = datetime.utcnow()
...: return default
...:
In [12]: foo()
Out[12]: datetime.datetime(2020, 7, 27, 1, 50, 50, 582885)
In [13]: foo()
Out[13]: datetime.datetime(2020, 7, 27, 1, 50, 56, 356420)
For the date, you can try datetime.date.today() or datetime.datetime.now().date as long as you have the timezone configured in your settings file. You can import like from datetime import datetime.
However, I still think it's easier if you give the model a datetimefield if you have a model.
It seems that you cannot replace
from django.core import serializers
from django.http import HttpResponse, JsonResponse
qs = MyModel.objects.filter(pk=1)
data = serializers.serialize('json', qs, fields=('id', 'name', 'my_m2m_field'))
# I want data of the one instance only.
data = data[data.find('{', 3, 15):data.rfind('}', -60, -3) + 1]
return HttpResponse(data, content_type='application/json')
by JsonResponse, can you? (I don't mean to replace just the last line by return JsonResponse(data) for this doesn't make sense in my opinion.)
For this results in an error:
my_m2m_ids = qs[0].my_m2m_field.all().values_list('id', 'flat=True') # = [3, 2]
dic = {'my_m2m_ids': my_m2m_ids} # also tried `my_m2m_ids[:]`
dic.update(qs.values('id', 'name')[0]) # also tried `list(qs.`… and `dict(qs.`…
return JsonResponse(dic) # also tried `safe=False`
Error: TypeError at <path> [3, 2] is not JSON serializable. I don't know exactly why but I think it's caused somehow by the ValuesQuerySet retruned by values() or by the ValuesListQuerySet returnd by values_list().
Is there any better/shorter solution? For I think it's both not ideal.
Update
It works after converting the ValuesListQuerySet to a list (by my_m2m_ids = list(my_m2m_ids), but my_m2m_ids[:] obviously doesn't work).
But I still loved to be able to use JsonResponse like this:
return JsonResponse(MyModel.objects.get(pk=1).only('id', 'name', 'my_m2m_field)) (or similar).
I would like to know how I can get ("decode?") the data from a JsonField, I'm having this:
{"pleople": "name=Jhon&email=email#domain.com", "id": 251304}
How I can pass this to my view like name['Jhon'] or any kind of object to use with querySet or parameter?
>>> from urlparse import parse_qs, parse_qsl
>>> parse_qs("name=Jhon&email=email#domain.com")
{'email': ['email#domain.com'], 'name': ['Jhon']} # allow multiple values
>>> dict(parse_qsl("name=Jhon&email=email#domain.com"))
{'email': 'email#domain.com', 'name': 'Jhon'} # dict w/ single value
Or you could use django.http.QueryDict directly
>>> from django.http import QueryQict
>>> QueryDict("name=Jhon&email=email#domain.com")
<QueryDict: {u'name': [u'Jhon'], u'email': [u'email#domain.com']}>
In Django: I have a date and time that I need to enter into my database model, which has a column models.DateTimeField(). It seems that no matter what I do, I get a ValidationError: enter a valid date/time format.
I have a string like this:
myStr = "2011-10-01 15:26"
I want to do:
p = mytable(myDate = WHAT_GOES_HERE)
p.save()
Please don't point me to a duplicate question. I have looked around and they point to other questions which again point to questions, which point to some documentaton, which just doesn't get me what I need. Thanks!
>>> import datetime
>>> myStr = "2011-10-01 15:26"
>>> WHAT_GOES_HERE = datetime.datetime.strptime(myStr, "%Y-%m-%d %H:%M")
>>> WHAT_GOES_HERE
datetime.datetime(2011, 10, 1, 15, 26)
>>>
datetime.strptime()
From the Django documentation:
# inserting datetime.now()
import django.utils.timezone as tz
mytable.objects.create(myDate=tz.localtime())
# inserting any date:
import pytz
import django.utils.timezone as tz
from datetime import datetime as dt
my_tz = pytz.timezone('Europe/Bucharest')
my_date = dt(2018, 8, 20, 0)
mytable.objects.create(myDate=tz.make_aware(my_date, my_tz))
You can simply do the following
myStr = '2011/10/01 15:26'
And then when creating your object just use myStr as an attribute value:
p = mytable(myDate = myStr)
p.save()