sql: use custom json encoder for computed data storage (#53772)

This commit is contained in:
Frédéric Péters 2021-05-05 15:08:43 +02:00
parent 4fe72ddb49
commit c05ae5877d
2 changed files with 34 additions and 2 deletions

View File

@ -1,4 +1,5 @@
import datetime
import decimal
import pytest
from django.utils.timezone import make_aware
@ -330,6 +331,36 @@ def test_computed_field_complex_data(pub, http_requests):
assert formdata.data['1'] == {'foo': 'bar'}
def test_computed_field_decimal_data(pub, http_requests):
FormDef.wipe()
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ComputedField(
id='1',
label='computed',
varname='computed',
value_template='{{ "123.45"|decimal }}',
freeze_on_initial_value=True,
),
fields.CommentField(id='2', label='X{{form_var_computed}}Y', type='comment'),
]
formdef.store()
formdef.data_class().wipe()
resp = get_app(pub).get('/test/')
assert 'X123.45Y' in resp.text
resp = resp.forms[0].submit('submit') # -> validation
resp = resp.forms[0].submit('submit').follow() # -> submit
assert 'The form has been recorded' in resp.text
assert formdef.data_class().count() == 1
formdata = formdef.data_class().select()[0]
# pickle storage keeps the typed data but postgresql storage has to convert
# it to stringfor json storage, hence the casting here:
assert decimal.Decimal(formdata.data['1']) == decimal.Decimal('123.45')
def test_computed_field_usage_in_live_data(pub, http_requests):
FormDef.wipe()

View File

@ -17,6 +17,7 @@
import copy
import datetime
import io
import json
import re
import time
import unicodedata
@ -47,7 +48,7 @@ from wcs.qommon import PICKLE_KWARGS, force_str
from . import qommon
from .publisher import UnpicklerClass
from .qommon import _, get_cfg
from .qommon.misc import strftime
from .qommon.misc import JSONEncoder, strftime
from .qommon.storage import _take, deep_bytes2str
from .qommon.storage import parse_clause as parse_storage_clause
from .qommon.substitution import invalidate_substitution_cache
@ -1700,7 +1701,7 @@ class SqlMixin:
if value is not None:
# embed value in a dict, so it's never necessary to cast the
# value for postgresql
value = {'data': value, '@type': 'computed-data'}
value = {'data': json.loads(JSONEncoder().encode(value)), '@type': 'computed-data'}
elif sql_type == 'varchar':
assert isinstance(value, str)
elif sql_type == 'date':