尝试使用数据库查询时,Django渠道Async Websocket引发错误

时间:2020-09-14 23:06:55

标签: python django django-channels

我不明白。即使我已经在异步等待中转换了所有必填字段。但仍然出现以下错误:

HTTP GET /chat/8/ 200 [0.02, 127.0.0.1:51354]
WebSocket HANDSHAKING /ws/chat/8/ [127.0.0.1:51356]
Exception inside application: You cannot call this from an async context - use a thread or sync_to_async.
Traceback (most recent call last):
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/sessions.py", line 183, in __call__
    return await self.inner(receive, self.send)
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/middleware.py", line 41, in coroutine_call
    await inner_instance(receive, send)
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/consumer.py", line 58, in __call__
    await await_many_dispatch(
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/utils.py", line 51, in await_many_dispatch
    await dispatch(result)
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/consumer.py", line 73, in dispatch
    await handler(message)
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/channels/generic/websocket.py", line 175, in websocket_connect
    await self.connect()
  File "/media/fahadmdkamal/WORK/B-DOPS/api/chat/consumers.py", line 23, in connect
    other_user = await sync_to_async(User.objects.get(id=others_id))
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/query.py", line 411, in get
    num = len(clone)
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/query.py", line 258, in __len__
    self._fetch_all()
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/query.py", line 1261, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/query.py", line 57, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1150, in execute_sql
    cursor = self.connection.cursor()
  File "/media/fahadmdkamal/WORK/B-DOPS/api/env/lib/python3.8/site-packages/django/utils/asyncio.py", line 24, in inner
    raise SynchronousOnlyOperation(message)
django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
WebSocket DISCONNECT /ws/chat/8/ [127.0.0.1:51356]

该错误表明我需要调用sync_to_async。即使我调用了该函数,我仍然会收到此错误。我想打印从数据库添加的其他用户。但这甚至不涉及该语句,这意味着我的数据库调用可能没有正确的方式。我已经看到了其他一些代码结构,这些结构似乎完全相同。我想念什么?

我的消费者代码:

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):

        # Get message sender information
        self.me = self.scope['user']
        # print(self.me)

        # Get other user's ID from the url
        others_id = self.scope['url_route']['kwargs']['user_id']
        # Get Other user object using the collected object.
        other_user = await   sync_to_async(User.objects.get(id=others_id))
        print(other_user)

        # Get or create personal thread for the conversation
        self.thread_obj = await sync_to_async(
            Thread.objects.get_or_create_personal_thread(
                self.me,
                other_user)
        )

        # Creating room with the thread object id.
        self.room_name = f'presonal_thread_{self.thread_obj.id}'

        # Adding room and channel name to to the channel layer group
        await self.channel_layer.group_add(
            self.room_name,
            self.channel_name
        )

        # Accecpting the connection
        await self.accept()

        print(f'[{self.channel_name}] - You are connected')

    # Codes that will be run while disconnecting the websocket
    async def disconnect(self, close_code):
        print(f'[{self.channel_name}] - Disonnected')

        # remove room and channel name from the channel layer
        await self.channel_layer.group_discard(
            self.room_name,
            self.channel_name
        )

    # Receive message from WebSocket
    async def receive(self, text_data):
        # Decode json raw data that was sent from user
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        print(f'[{self.channel_name}] - Recieved message - {message}')

        # Store the received data
        await self.store_message(message=message)

        # Send message to room group so that it can send to the all users
        await self.channel_layer.group_send(
            self.room_name,
            {
                'type': 'chat_message',
                'message': message,
            }
        )

    # Receive message from room group
    async def chat_message(self, event):
        print(f'[{self.channel_name}] - Message sent - {event["message"]}')

        message = event['message']

        # Send message to WebSocket
        await self.send(text_data=json.dumps({
            'message': message,
            'user_id': self.scope['user'].id,
            'username': self.scope['user'].username
        }))

    @database_sync_to_async
    def store_message(self, message):
        Message.objects.create(sender=self.me,
                               thread=self.thread_obj,
                               message=message)


我的路由代码:

application = ProtocolTypeRouter({
    'websocket': AuthMiddlewareStack(
        URLRouter([
            path('ws/chat/<int:user_id>/', consumers.ChatConsumer)
        ])
    ),
})

由于我是django频道的新手,所以如果您能说整个AsyncWebsocketConsumer结构还可以的话,这将更加有用。

1 个答案:

答案 0 :(得分:0)

哦..解决了..

实际上,我在构造await sync_to_async调用时不正确。 该函数应单独调用,参数应在单独的括号中调用。

错误的结构:

other_user = await   sync_to_async(User.objects.get(id=others_id))

更新后的结构:

other_user = await sync_to_async(User.objects.get)(id=others_id)

在这里,我要调用sync_to_async方法以在第一个括号中获取用户对象,而在第二个括号中,我要提供查询参数。