Skip to content

Commit 0e437c9

Browse files
committed
Serialize floats in JSON as strings so they aren't converted to integers in JavaScript.
1 parent aaf62e8 commit 0e437c9

File tree

2 files changed

+42
-3
lines changed

2 files changed

+42
-3
lines changed

django_unicorn/components.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ def render(self):
104104

105105
content = response.content.decode("utf-8")
106106

107-
checksum = generate_checksum(str.encode(str(self.frontend_context_variables)))
107+
frontend_context_variables_dict = orjson.loads(self.frontend_context_variables)
108+
checksum = generate_checksum(orjson.dumps(frontend_context_variables_dict))
108109

109110
soup = BeautifulSoup(content, features="html.parser")
110111
root_element = UnicornTemplateResponse._get_root_element(soup)

django_unicorn/serializer.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any
1+
from typing import Any, Dict, List
22

33
from django.core.serializers import serialize
44
from django.db.models import Model, QuerySet
@@ -48,13 +48,51 @@ def _json_serializer(obj):
4848
raise TypeError
4949

5050

51+
def _fix_floats(current: Dict, data: Dict = None, paths: List = []) -> None:
52+
"""
53+
Recursively change any Python floats to string so that JavaScript
54+
won't change float to integers.
55+
56+
Params:
57+
current: Dictionary in which to check for and fix floats.
58+
"""
59+
60+
if data is None:
61+
data = current
62+
63+
if isinstance(current, dict):
64+
for key, val in current.items():
65+
paths.append(key)
66+
_fix_floats(val, data, paths=paths)
67+
paths.pop()
68+
elif isinstance(current, list):
69+
for (idx, item) in enumerate(current):
70+
paths.append(idx)
71+
_fix_floats(item, data, paths=paths)
72+
paths.pop()
73+
elif isinstance(current, float):
74+
_piece = data
75+
76+
for (idx, path) in enumerate(paths):
77+
if idx == len(paths) - 1:
78+
# `path` can be a dictionary key or list index,
79+
# but either way it is retrieved the same way
80+
_piece[path] = str(current)
81+
else:
82+
_piece = _piece[path]
83+
84+
5185
def dumps(data: dict) -> str:
5286
"""
5387
Converts the passed-in dictionary to a string representation.
5488
5589
Handles the following objects: dataclass, datetime, enum, float, int, numpy, str, uuid,
5690
Django Model, Django QuerySet, any object with `to_json` method.
5791
"""
58-
dumped_data = orjson.dumps(data, default=_json_serializer).decode("utf-8")
92+
serialized_data = orjson.dumps(data, default=_json_serializer)
93+
dict_data = orjson.loads(serialized_data)
94+
_fix_floats(dict_data)
95+
96+
dumped_data = orjson.dumps(dict_data).decode("utf-8")
5997

6098
return dumped_data

0 commit comments

Comments
 (0)