I read everything I could found about serializing QuerySets in Django but still can not find any working solution for transferring the existing WSGI function into a ASGI WebSocket Consumer.
My view function looked like this:
...
context = {
"registered": user_is_player,
"object": game,
"player": player,
"players": game.player_set.all(),
"players_cards": players_cards,
"card_deck": game.globalcarddeck_set.all(),
"prio_deck": game.prioritydeck_set.all(),
"table": game.table_set.all(),
"cards": game.card_set.all(),
}
return render(request=request, template_name=template_name, context=context)
and was working fine, but the query results sadly seam not to be transferable via synchronous web socket.
Within my class GameConsumer(WebsocketConsumer)
I figured that I can
transfer a single result with with model_to_dict (after sadly loosing a few fields) but same does not apply to QuerySets. Those can be only serialized with the serialize function like: serialize("json", game_instance.player_set.all())
.
... game_instance = Game.objects.get(slug=self.game_name) game_json = model_to_dict( game_instance, fields=["name", "slug", "round_number", "active", "started"] )
context = {
"registered": user_is_player,
"game": game_json,
"player": player,
"message": message,
"players": serialize("json", game_instance.player_set.all()),
"players_cards": serialize("json", players_cards),
"card_deck": serialize("json", game_instance.globalcarddeck_set.all()),
"prio_deck": serialize("json", game_instance.prioritydeck_set.all()),
"table": serialize("json", game_instance.table_set.all()),
"cards": serialize("json", game_instance.card_set.all()),
}
self.send(text_data=json.dumps({"context": context}))
At the end everything needs to be dumped with json.dumps, otherwise one would end up with the exception:
self.sendMessage(content.encode("utf8"), binary)
AttributeError: 'dict' object has no attribute 'encode'
This seam to be the only way to get the context serialized over the socket, but the end result makes me cry:
{"context":{"registered":true,"game":{"name":"third","slug":"third","round_number":0,"active":true,"started":true},"player":{"id":17,"cardholder_ptr":17,"game":3,"name":"Maciek","dam":0,"idm":0,"round_score":0,"game_score":0},"message":null,"players":"{\"model\": \"suena1M.player\",\"pk\": 17,\"fields\": {\"game\": 3,\"name\": \"Maciek\",\"dam\": 0,\"idm\": 0,\"round_score\": 0,\"game_score\": 0}}\n{\"model\": \"suena1M.player\",\"pk\": 19,\"fields\": {\"game\": 3,\"name\": \"Peter\",\"dam\": 0,\"idm\": 0,\"round_score\": 0,\"game_score\": 0}}\n{\"model\": \"suena1M.player\",\"pk\": 21,\"fields\": {\"game\": 3,\"name\": \"Petra\",\"dam\": 0,\"idm\": 0,\"round_score\": 0,\"game_score\": 0}}\n","players_cards":"[]","card_deck":"[{\"model\": \"suena1M.globalcarddeck\", \"pk\": 23, \"fields\": {\"game\": 3}}, {\"model\": \"suena1M.globalcarddeck\", \"pk\": 26, \"fields\": {\"game\": 3}}, {\"model\": \"suena1M.globalcarddeck\", \"pk\": 29, \"fields\": {\"game\": 3}}, {\"model\": \"suena1M.globalcarddeck\", \"pk\": 32, \"fields\": {\"game\": 3}}, {\"model\": \"suena1M.globalcarddeck\", \"pk\": 35, \"fields\": {\"game\": 3}}, {\"model\": \"suena1M.globalcarddeck\", \"pk\": 38, \"fields\": {\"game\": 3}}, {\"model\": \"suena1M.globalcarddeck\", \"pk\": 41, \"fields\": {\"game\": 3}}, {\"model\": \"suena1M.globalcarddeck\", \"pk\": 44, \"fields\": {\"game\": 3}},
Basically everything, what has been serialized with serialized is not usable from within the template because of the escaping hell.
Please, I'm new to Django and Django Channels, maybe there is a batter way to pass ORM Results over the sockets - but for now, I'm out of ideas.
UPDATE
I am using the following serializers:
from django.forms.models import model_to_dict
from django.core.serializers import serialize
and the build in json.dumps
method
CodePudding user response:
WebsocketConsumer.send()
accepts either text_data
or bytes_data
. You cannot pass QuerySet
into .send()
as it only accepts strings. so you need to serialize your data into json and parse it on the client side.
Also, the escaping is because your are double encoding some of your data; once with .serialize()
method, then with json.dumps()
.
I suggest using Django Rest Framework's Serializers. This way you choose which fields you want to serialize. Please refer to this link: https://www.django-rest-framework.org/api-guide/serializers/
CodePudding user response:
Yeay - once I have adopted @Smil23 solution as following:
declared a ModelSerializer like this:
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
model = Player
fields = "__all__"
and replaced calls like
"players": serialize("json", game_instance.player_set.all()),
by
"players": PlayerSerializer(game_instance.player_set.all(), many=True).data,
I finally got correctly looking data transferred to the page via the socket :)
Thx!