Json.dumps сериализованный набор запросов в непригодный для использования формат в Django с WebSockets (Каналы)
Я прочитал все, что смог найти о сериализации QuerySets в Django, но все еще не могу найти никакого рабочего решения для переноса существующей WSGI функции в ASGI WebSocket Consumer.
Моя функция просмотра выглядела следующим образом:
...
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)
и работал нормально, но результаты запроса, к сожалению, не могут быть переданы через синхронный веб-сокет.
В ходе своей class GameConsumer(WebsocketConsumer) работы я понял, что я могу
передать один результат с помощью model_to_dict (к сожалению, потеряв несколько полей), но то же самое не относится к QuerySets. Их можно только сериализовать с помощью функции serialize, например: 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}))
В конце все должно быть выгружено с помощью json.dumps, иначе возникнет исключение:
self.sendMessage(content.encode("utf8"), binary)
AttributeError: 'dict' object has no attribute 'encode'
Это единственный способ получить сериализованный контекст через сокет, но конечный результат заставляет меня плакать:
{"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}},
В основном все, что было сериализовано с помощью serialized, не может быть использовано из шаблона из-за ада экранирования.
Пожалуйста, я новичок в Django и Django Channels, может быть, есть лучший способ передать результаты ORM через сокеты - но пока что у меня нет идей.
WebsocketConsumer.send() принимает либо text_data, либо bytes_data. Вы не можете передать QuerySet в .send(), так как он принимает только строки. Поэтому вам нужно сериализовать данные в json и разобрать их на стороне клиента.
Кроме того, экранирование происходит потому, что вы дважды кодируете некоторые данные; один раз с помощью метода .serialize(), затем с помощью json.dumps().
Да - однажды я принял решение @Smil23 следующим образом:
объявили ModelSerializer следующим образом:
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
model = Player
fields = "__all__"
и заменил вызовы типа
"players": serialize("json", game_instance.player_set.all()),
by
"players": PlayerSerializer(game_instance.player_set.all(), many=True).data,
Я наконец-то получил правильно выглядящие данные, передаваемые на страницу через сокет :)
Thx!