首页 > 解决方案 > Django Rest 框架和通道,您不能从异步上下文中调用它

问题描述

我想要做的是将一个 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']

错误 :

应用程序内部的异常:您不能从异步上下文中调用它 - 使用线程或 sync_to_async。回溯(最近一次调用):文件“C:\Users\baza\Desktop\production\venv\lib\site-packages\django\db\models\fields\related_descriptors.py”,第 173 行,在获取 rel_obj = self .field.get_cached_value(instance) 文件“C:\Users\baza\Desktop\production\venv\lib\site-packages\django\db\models\fields\mixins.py”,第 15 行,在 get_cached_value 返回 instance._state .fields_cache[cache_name] KeyError : 'user_01'

这在没有 Django Chennels 的情况下正常工作,因为通道是异步的,我不能使用同步代码,使用以下方法可以正常工作:

os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "真"

在设置文件中,但在生产方面这不是一种安全的方法。我尝试使用通道的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)])]

带着

应用程序内部异常:“协程”类型的对象不是 JSON 可序列化的

所以我猜 JSON 无法序列化那些“协程”对象,通常我应该或“想要”获取它们的实际值。

我怎样才能完成任务?欢迎任何解决方法或其他方法,在此先感谢您。

在消费者.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())

标签: djangoasynchronousdjango-rest-frameworkdjango-channels

解决方案


我从未使用过 Django Channels,但我知道 Django 和异步。老实说,我不喜欢这些 hacky 装饰器。而且我不认为在线程中运行这样一个简单的任务是一个好主意。

您有一个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魔法有任何问题,它更有效,更容易推理。


编辑:

我再说一遍,你应该选择相关的数据。添加另一个示例后,我可以提出类似的建议

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

推荐阅读