【问题标题】:Django Rest Framework and Channels, You cannot call this from an async contextDjango Rest 框架和通道,您不能从异步上下文中调用它
【发布时间】:2023-03-03 04:07:01
【问题描述】:

我想要做的是将一个 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。 Traceback(最近的通话 最后):文件 "C:\Users\baza\Desktop\production\venv\lib\site-packages\django\db\models\fields\related_descriptors.py", 第 173 行,在 get 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 中 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)])]

有一个

应用程序内部异常:“协程”类型的对象不是 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())

【问题讨论】:

    标签: django asynchronous django-rest-framework django-channels


    【解决方案1】:

    我从未使用过 Django Channels,但我知道 Django 和 async。 老实说,我不喜欢这些 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']
    

    【讨论】:

    • SerializerMethodField 查找 get_{{field_name}} 并默认将当前对象作为参数传递,这些对象来自我在 consumer.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) }) 中的 APIVIEW PS:user_01 = serializers .CharField(source='user_01.username') 不起作用
    • 是的,所以如果您在APIView 中使用select_related,您可以使用我写的或您的SerializerMethodField,因为user_01 将在序列化期间在obj 中可用。显示你的 APIView 你在哪里使用这个序列化程序。
    • 对不起,我的意思是消费者而不是查看
    • 无论哪种方式,显示您使用此序列化程序的位置
    • 像魅力一样工作!先生,您是救生员,非常感谢。
    猜你喜欢
    • 2022-11-27
    • 2020-09-07
    • 2019-05-23
    • 2021-12-23
    • 1970-01-01
    • 2016-02-16
    • 2020-12-20
    • 2016-10-12
    • 2017-01-25
    相关资源
    最近更新 更多