How to test Django JSON-ified ISO8601 Datetime - django

I'm testing with AjaxResponse with my request factory on a datetime. The problem is the string that Django gives is like this: 2020-08-27T22:46:07.354Z
But when I have datetime object, and I use the isoformat() method, I don't get the same string: 2020-08-27T22:46:07.354734+00:00
How am I going to be able to assert? I'm looking to assert by comparing the JSON with my own Python list (The list is what I can customize).

Thanks to Bast in Discord.py; the datetime conversion is not even iso8601 entirely... it's defined by ECMA-262 which is for JS:
from django.core.serializers.json import DjangoJSONEncoder
DjangoJSONEncoder().default(datetime_obj)
>>> '2020-08-28T03:41:59.194Z'
whereas using the .isoformat() method from standard lib would return 6 rather than 3 decimal points and use +00:00 rather than Z.

Related

Get max and min formatted date values from queryset in Django

I have a one to many relation between session and camp. Now I have to get the max and min dates of all camps combined for a particular session.
I am able to do it like this:
sess = Session.objects.last()
max_min_dates = sess.camp.aggregate(Min('start_date'), Max('end_date'))
But if I try to send this from HttpResponse then I am getting this error:
TypeError: Object of type 'date' is not JSON serializable
So I need to send the formatted date values in that. How can I modify the above code to get the same?
The default encoder for json.dumps() does not support date encoding (ie. can't convert date into str). You can use django encoder instead, it supports a few more data types see the link for more info.
Django Ref
import json
from django.core.serializers.json import DjangoJSONEncoder
json_str = json.dumps(max_min_dates, cls=DjangoJSONEncoder)

Django JSONField - get source text

When using a JSONField, the contents are automatically decoded from JSON into python objects when reading the value. I have a use-case where I'm encoding the string back into JSON to embed in a template. Is there any way to just get the raw JSON string from the object?
Django uses psycopg2.extras.Json under the hood. You will need to cast the field as text to get the original out as plain text [1]. Use django's Cast function [2] to annotate your queryset:
from django.db.models.functions import Cast
from django.db.models import TextField
models_with_json_text = Model.objects.annotate(
json_as_text=Cast("json_field_name", TextField())
)
[1] http://initd.org/psycopg/docs/extras.html#json-adaptation
[2] https://docs.djangoproject.com/en/2.2/ref/models/database-functions/#cast

python date string object to datetime object

I get following response from an API:
/Date(1503964800000+0000)/
It is a string object. How do I convert it in to something like
2017-08-11T00:00:00
You can use datetime.datetime.fromtimestamp]1 in combination with strftime:
import datetime
print(
datetime.datetime.fromtimestamp(int("1502440590")).strftime("%Y-%m-%dT%H:%M:%S")
)
The result:
2017-08-11T10:36:30

How to serialize to JSON list of tuples containing Decimal in Django?

I have a Django model containing DecimalField. The resulting json should contain only data (no keys) so I'm using values_list() to convert queryset to list of Tuples:
MyModel.objects.filter(...).values_list('my_date_field','my_decimal_field').order_by('my_date_field')
Then, I need to serialize it to json... but json.dumps does not seems to be able to process the Decimal field... A lot of SO answers about that suggest to make your own encoder to use with json.dumps but those custom encoders are not recursive and seems not to work with a list of Tuple...
What I need is returning json with this format:
[[1162512000000,78.29],
[1162771200000,79.71],
[1162857600000,80.51],
[1162944000000,82.45],
[1163030400000,83.34],
[1163116800000,83.12],
[1163376000000,84.35]]
It seems to me that this should be a simple task but can't find a simple way to do it without having to parse and process everything manually...
Any suggestions?
Thanks a lot
Etienne
This should work:
import json
from decimal import Decimal as D
class DecimalJSONEncoder(json.JSONEncoder):
def default(self, o):
if type(o) == D:
# Here You can decide if You want decimal to be converted
# to string or float.
return float(o)
return super(DecimalJSONEncoder, self).default(o)
data = [[1162512000000, D(78.29)],
[1162771200000, D(79.71)],
[1162857600000, D(80.51)],
[1162944000000, D(82.45)],
[1163030400000, D(83.34)],
[1163116800000, D(83.12)],
[1163376000000, D(84.35)]]
encoder = DecimalJSONEncoder()
encoder.encode(data)
# Result:
# '[[1162512000000, 78.29], [1162771200000, 79.71], [1162857600000, 80.51], ...'

django escapejs and simplejson

I'm trying to encode a Python array into json using simplejson.dumps:
In [30]: s1 = ['test', '<script>']
In [31]: simplejson.dumps(s1)
Out[31]: '["test", "<script>"]'
Works fine.
But I want to escape the strings first (using escapejs from Django) before calling simplejson.dumps:
In [35]: s_esc
Out[35]: [u'test', u'\\u003Cscript\\u003E']
In [36]: print simplejson.dumps(s_esc)
["test", "\\u003Cscript\\u003E"]
My problem is: I want the escaped string to be: ["test", "\u003Cscript\u003E"] instead of ["test", "\\u003Cscript\\u003E"]
I can use replace:
In [37]: print simplejson.dumps(s_esc).replace('\\\\', '\\')
["test", "\u003Cscript\u003E"]
But is this a good approach? I just want to escape the strings first before encoding them to json. So there will be no syntax errors when I use them in template.
Thanks. :)
simplejson 2.1.0 and later include a JSONEncoderForHTML encoder that does exactly what you want. To use it in your example:
>>> s1 = ['test', '<script>']
>>> simplejson.dumps(s1, cls=simplejson.encoder.JSONEncoderForHTML)
'["test", "\\u003cscript\\u003e"]'
I ran into this recently where I didn't have control over the code that was generating the data structures, so I couldn't escape the strings as they were being assembled. JSONEncoderForHTML solved the problem neatly at the point of output.
Of course, you'll need to have simplejson 2.1.0 or later. (Django used to come with an older version, and Django 1.5 deprecated django.utils.simplejson entirely.) If you can't upgrade for some reason, the JSONEncoderForHTML code is relatively small and could probably be pulled into earlier code or used with Python 2.6+'s json package -- though I haven't tried this myself
You're doing the operations in the wrong order. You should dump your data to a JSON string, then escape that string. You can do the escaping with the addslashes Django filter.