djangochannelsrestframework
djangochannelsrestframework copied to clipboard
[BUG] TypeError: can not serialize 'UUID' object models with pk type uuid
Describe the bug
When looking at a model and it has a pk type UUID it causes the exception TypeError: can not serialize 'UUID' object
in chanels redis
class MyModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
LOG
File "/code/djangochannelsrestframework/observer/model_observer.py", line 143, in post_change_receiver
mmcb | self.send_messages(
mmcb | File "/code/djangochannelsrestframework/observer/model_observer.py", line 166, in send_messages
mmcb | async_to_sync(channel_layer.group_send)(group_name, message_to_send)
mmcb | File "/usr/local/lib/python3.10/site-packages/asgiref/sync.py", line 218, in __call__
mmcb | return call_result.result()
mmcb | File "/usr/local/lib/python3.10/concurrent/futures/_base.py", line 451, in result
mmcb | return self.__get_result()
mmcb | File "/usr/local/lib/python3.10/concurrent/futures/_base.py", line 403, in __get_result
mmcb | raise self._exception
mmcb | File "/usr/local/lib/python3.10/site-packages/asgiref/sync.py", line 284, in main_wrap
mmcb | result = await self.awaitable(*args, **kwargs)
mmcb | File "/code/channels_redis/core.py", line 682, in group_send
mmcb | ) = self._map_channel_keys_to_connection(channel_names, message)
mmcb | File "/code/channels_redis/core.py", line 782, in _map_channel_keys_to_connection
mmcb | channel_key_to_message[key] = self.serialize(value)
mmcb | File "/code/channels_redis/core.py", line 802, in serialize
mmcb | value = msgpack.packb(message, use_bin_type=True)
mmcb | File "/usr/local/lib/python3.10/site-packages/msgpack/__init__.py", line 38, in packb
mmcb | return Packer(**kwargs).pack(o)
mmcb | File "msgpack/_packer.pyx", line 294, in msgpack._cmsgpack.Packer.pack
mmcb | File "msgpack/_packer.pyx", line 300, in msgpack._cmsgpack.Packer.pack
mmcb | File "msgpack/_packer.pyx", line 297, in msgpack._cmsgpack.Packer.pack
mmcb | File "msgpack/_packer.pyx", line 231, in msgpack._cmsgpack.Packer._pack
mmcb | File "msgpack/_packer.pyx", line 231, in msgpack._cmsgpack.Packer._pack
mmcb | File "msgpack/_packer.pyx", line 291, in msgpack._cmsgpack.Packer._pack
mmcb | TypeError: can not serialize 'UUID' object
Additional context This is my code that I use to observe the model with pk type UUID
from djangochannelsrestframework.generics import GenericAsyncAPIConsumer
from djangochannelsrestframework.observer.generics import ObserverModelInstanceMixin
from .models import MyModel
from .serializers import MySerializer
class MyRetrieveConsumer(ObserverModelInstanceMixin, GenericAsyncAPIConsumer):
queryset = MyModel.objects.all()
serializer_class = MySerializer
I solved it by checking if the pk value of the model is a UUID type and convert it to a string
class ModelObserver(BaseObserver):
.
.
.
.
.
.
def serialize(self, instance, action, **kwargs) -> Dict[str, Any]:
message_body = {}
if self._serializer:
message_body = self._serializer(self, instance, action, **kwargs)
elif self._serializer_class:
message_body = self._serializer_class(instance).data
else:
message_body["pk"] = instance.pk
# THIS IS MY SOLUTION FOR NOW
# Check if the pk of the model is UUID type and convert it to a string
if isinstance(message_body['pk'], UUID):
message_body.update({'pk': str(message_body['pk'])})
message = dict(
type=self.func.__name__.replace("_", "."),
body=message_body,
action=action.value,
)
return message
I think I actually have a similar issue, but with a different field
type.
In my case, I set the COERCE_DECIMAL_TO_STRING
of the REST_FRAMEWORK
settings to FALSE
, as we would like to use actual numbers in JSON format, not a stringified version.
If I use the view_as_consumer
function to wrap the underlying ViewSet
of the model we created for the REST API, it does not raise the same error, but instead it just wraps the whole data object in quotes... This then looks like the following:
{
"errors": [],
"data": "{\"test\": \"test\"}",
"action": "list",
"response_status": 200,
"request_id": "12345"
}
This is of course also not the expected behavior...
That's why I investigated a little and tried setting COERCE_DECIMAL_TO_STRING
to TRUE
again (the default value). Then, the JSON is correctly returned.
As I tested a little more with an explicit Consumer
with the queryset
and the serializer_class
set in the same way as in the ViewSet
used in the view_as_consumer
call, a similar exception to the one mentioned in this issue is thrown, but instead of UUID object
, it remarks Object of type Decimal is not JSON serializable
.
This might therefore need another test and special handling like the one introduced in #155, correct?
@pollitux If I understand your problem well, it seems to me that it comes from the serialization. msgpack works only with primitive types. You can try with another serialization library. Personally I use orjson.
class MyModelConsumer(ObserverModelInstanceMixin, GenericAsyncAPIConsumer):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
@classmethod
async def decode_json(cls, text_data):
return orjson.loads(text_data)
@classmethod
async def encode_json(cls, content):
return orjson.dumps(content).decode()