Django Rest Framework 和 Channels,您不能从异步上下文中调用它
Django Rest Framework and Channels, You cannot call this from an async context
我想做的是像这样将 DRF 模型序列化器嵌套到另一个模型序列化器的字段中
class username_serial(ModelSerializer):
class Meta:
model = User
fields = ['username','email']
class game_serial(ModelSerializer):
user_01 = username_serial()
class Meta:
model = game
fields = ['id','user_01','user_02','is_private','is_accepted']
错误:
Exception inside application: You cannot call this from an async
context - use a thread or sync_to_async. Traceback (most recent call
last): File
"C:\Users\baza\Desktop\production\venv\lib\site-packages\django\db\models\fields\related_descriptors.py",
line 173, in get
rel_obj = self.field.get_cached_value(instance) File "C:\Users\baza\Desktop\production\venv\lib\site-packages\django\db\models\fields\mixins.py",
line 15, in get_cached_value
return instance._state.fields_cache[cache_name] KeyError: 'user_01'
这在没有 Django Chennels 的情况下正常工作,因为通道是异步的,我不能使用同步代码,通过使用可以正常工作:
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
在设置文件中,但在生产环境中这不是一种安全的方法。
我尝试使用通道的 database_sync_to_async 作为装饰器以及带有 SerializerMethodField 的函数,如下所示:
class game_serial(ModelSerializer):
user_01 = SerializerMethodField(read_only=True)
@database_sync_to_async
def get_user_01(self, obj):
username = obj.user_01.username
return str(username)
class Meta:
model = game
fields = ['id','user_01','user_02','is_private','is_accepted']
但我回来了:
[OrderedDict([('id', 23), ('user_01', <coroutine object SyncToAsync.__call__ at 0x0000000005DF6678>), ('user_02', None), ('is_private', False), ('is_accepted', False)]), OrderedDict([('id', 24), ('user_01', <coroutine object SyncToAsync.__call__ at 0
x0000000005DF6D58>), ('user_02', None), ('is_private', False), ('is_accepted', False)])]
有一个
Exception inside application: Object of type 'coroutine' is not JSON
serializable
所以我猜 JSON 无法序列化那些“协程”对象,通常我应该或“想要”获取它们的实际值。
我该如何完成任务?欢迎使用任何变通方法或其他方法,提前致谢。
在consumers.py..
games = await database_sync_to_async(self.get_games)()
serialized_games = game_serial(games, many=True)
await self.send({
"type": "websocket.send",
'text': json.dumps(serialized_games.data)
})
def get_games(self):
return list(game.objects.all())
我从未使用过 Django Channels,但我知道 Django 和异步。
老实说,我不喜欢这些古怪的装饰器。而且我不认为 运行 在一个线程中完成如此简单的任务是个好主意。
您有一个 obj
,因此您可以更早地查询数据库。如果是这样,则有一个地方没有异步上下文或异步上下文可以访问数据库。在错误消息中,user_01
未在数据库的“缓存”对象中找到。因此,只需预取您需要的内容即可。
def get_queryset(self):
return game_serial.objects.select_related('user_01')
class game_serial(ModelSerializer):
user_01 = serializers.CharField(source='user_01.username')
这样你就不会遇到这个 sync-to-async
魔术的问题,它更有效,更容易推理。
编辑:
我再说一遍,您应该 select 与获取数据的位置相关。在你添加另一个示例后,我可以建议类似的东西
def get_games(self):
return list(game.objects.select_related('user_01').all())
它会工作得很好。
你也可以试试
@database_sync_to_async
def get_games(self):
return list(game.objects.select_related('user_01').all())
和
serialized_games = await game_serial(games, many=True)
在这两种情况下,此序列化程序都可以正常工作。
class game_serial(ModelSerializer):
user_01 = serializers.CharField(source='user_01.username')
class Meta:
model = game
fields = ['id','user_01','user_02','is_private','is_accepted']
我想做的是像这样将 DRF 模型序列化器嵌套到另一个模型序列化器的字段中
class username_serial(ModelSerializer):
class Meta:
model = User
fields = ['username','email']
class game_serial(ModelSerializer):
user_01 = username_serial()
class Meta:
model = game
fields = ['id','user_01','user_02','is_private','is_accepted']
错误:
Exception inside application: You cannot call this from an async context - use a thread or sync_to_async. Traceback (most recent call last): File "C:\Users\baza\Desktop\production\venv\lib\site-packages\django\db\models\fields\related_descriptors.py", line 173, in get rel_obj = self.field.get_cached_value(instance) File "C:\Users\baza\Desktop\production\venv\lib\site-packages\django\db\models\fields\mixins.py", line 15, in get_cached_value return instance._state.fields_cache[cache_name] KeyError: 'user_01'
这在没有 Django Chennels 的情况下正常工作,因为通道是异步的,我不能使用同步代码,通过使用可以正常工作:
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
在设置文件中,但在生产环境中这不是一种安全的方法。 我尝试使用通道的 database_sync_to_async 作为装饰器以及带有 SerializerMethodField 的函数,如下所示:
class game_serial(ModelSerializer):
user_01 = SerializerMethodField(read_only=True)
@database_sync_to_async
def get_user_01(self, obj):
username = obj.user_01.username
return str(username)
class Meta:
model = game
fields = ['id','user_01','user_02','is_private','is_accepted']
但我回来了:
[OrderedDict([('id', 23), ('user_01', <coroutine object SyncToAsync.__call__ at 0x0000000005DF6678>), ('user_02', None), ('is_private', False), ('is_accepted', False)]), OrderedDict([('id', 24), ('user_01', <coroutine object SyncToAsync.__call__ at 0
x0000000005DF6D58>), ('user_02', None), ('is_private', False), ('is_accepted', False)])]
有一个
Exception inside application: Object of type 'coroutine' is not JSON serializable
所以我猜 JSON 无法序列化那些“协程”对象,通常我应该或“想要”获取它们的实际值。
我该如何完成任务?欢迎使用任何变通方法或其他方法,提前致谢。
在consumers.py..
games = await database_sync_to_async(self.get_games)()
serialized_games = game_serial(games, many=True)
await self.send({
"type": "websocket.send",
'text': json.dumps(serialized_games.data)
})
def get_games(self):
return list(game.objects.all())
我从未使用过 Django Channels,但我知道 Django 和异步。 老实说,我不喜欢这些古怪的装饰器。而且我不认为 运行 在一个线程中完成如此简单的任务是个好主意。
您有一个 obj
,因此您可以更早地查询数据库。如果是这样,则有一个地方没有异步上下文或异步上下文可以访问数据库。在错误消息中,user_01
未在数据库的“缓存”对象中找到。因此,只需预取您需要的内容即可。
def get_queryset(self):
return game_serial.objects.select_related('user_01')
class game_serial(ModelSerializer):
user_01 = serializers.CharField(source='user_01.username')
这样你就不会遇到这个 sync-to-async
魔术的问题,它更有效,更容易推理。
编辑:
我再说一遍,您应该 select 与获取数据的位置相关。在你添加另一个示例后,我可以建议类似的东西
def get_games(self):
return list(game.objects.select_related('user_01').all())
它会工作得很好。
你也可以试试
@database_sync_to_async
def get_games(self):
return list(game.objects.select_related('user_01').all())
和
serialized_games = await game_serial(games, many=True)
在这两种情况下,此序列化程序都可以正常工作。
class game_serial(ModelSerializer):
user_01 = serializers.CharField(source='user_01.username')
class Meta:
model = game
fields = ['id','user_01','user_02','is_private','is_accepted']