viur-framework / viur-core

The core component of ViUR, the Python framework for modern web development.
https://www.viur.dev
MIT License
13 stars 14 forks source link

`compute` doesn't work in a `JsonBone` #1092

Open sveneberth opened 6 months ago

sveneberth commented 6 months ago

compute is implemented in serialize and unserialize, but the JsonBone overwrites this without a super call. So bone._compute is never called and the bone value is None.

ArneGudermann commented 6 months ago

Solved by #1093

sveneberth commented 1 week ago

Hey @ArneGudermann,

I tested it again and found a confusing behaivior.

I have this bone (has row=True by default):

    price = JsonBone(  
        compute=Compute(lambda skel: skel.price_.to_dict(), ComputeInterval(ComputeMethod.Always))
    )

This will use JsonBone.singleValueUnserialize which tries to load a json string and fails (it's a dict):

[2024-09-03 16:49:37,983] /.../viur/core/request.py:332 [ERROR] the JSON object must be str, bytes or bytearray, not dict
Traceback (most recent call last):
  File "/.../viur-core/src/viur/core/request.py", line 306, in _process
    self._route(path)
  File "/.../viur-core/src/viur/core/request.py", line 565, in _route
    res = caller(*self.args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/.../viur-core/src/viur/core/module.py", line 299, in __call__
    return self._func(self._instance, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/.../viur-shop/src/viur/shop/modules/api.py", line 272, in cart_list
    child = self.json_renderer.renderSkelValues(child_skel)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/.../viur-core/src/viur/core/render/json/default.py", line 137, in renderSkelValues
    res[key] = self.renderBoneValue(bone, skel, key)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/.../viur-core/src/viur/core/render/json/default.py", line 102, in renderBoneValue
    boneVal = skel[key]
              ~~~~^^^^^
  File "/.../viur-core/src/viur/core/skeleton.py", line 226, in __getitem__
    boneInstance.unserialize(self, key)
  File "/.../viur-core/src/viur/core/bones/base.py", line 815, in unserialize
    skel.accessedValues[name] = self._compute(skel, name)
                                ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/.../viur-core/src/viur/core/bones/base.py", line 1295, in _compute
    return unserialize_raw_value(ret)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/.../viur-core/src/viur/core/bones/base.py", line 1287, in unserialize_raw_value
    return self.singleValueUnserialize(raw_value)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/.../viur-core/src/viur/core/bones/json.py", line 49, in singleValueUnserialize
    return utils.json.loads(val)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/.../viur-core/src/viur/core/utils/json.py", line 90, in loads
    return json.loads(s, object_hook=object_hook, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/json/__init__.py", line 339, in loads
    raise TypeError(f'the JSON object must be str, bytes or bytearray, '
TypeError: the JSON object must be str, bytes or bytearray, not dict

So it failes to keep the raw value as it is.

With raw=False it works, because fromClient can handle the dict.

    price = JsonBone(  
        compute=Compute(lambda skel: skel.price_.to_dict(), ComputeInterval(ComputeMethod.Always), raw=False)
    )

I think raw: True should work, what do u think?

ArneGudermann commented 1 week ago

Mhh maybe we can add a check for this like:

    def singleValueUnserialize(self, val):
        if isinstance(val, (str,bytes,bytearray)):
            return utils.json.loads(val)

        return utils.json.loads(utils.json.dumps(val))

or

    def singleValueUnserialize(self, val):
        if isinstance(val, (str,bytes,bytearray)):
            return utils.json.loads(val)
        return val