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']