首页 > 解决方案 > 应用程序内部的异常:您不能从异步上下文中调用它 - 使用线程或 sync_to_async

问题描述

我一直在使用 django-channels,我使用单线程为 2 个用户创建了一个房间

from django.db import models
from django.contrib.auth import get_user_model
from django.db.models import Q
from channels.db import SyncToAsync
# Create your models here.
model = get_user_model()





class threadmanager(models.Manager):
    
    
    def get_or_create(self,firstuser,seconduser):

        
        thread = self.get_queryset().filter(((Q(first=firstuser)and Q(second = seconduser ))) or ((Q(first=seconduser)and Q(second = firstuser ))))
        if thread:
            print(thread.first())
            return thread 
        thread = self.model(first=firstuser,second=seconduser)    
        thread.save()
        return(thread)


class thread(models.Model):

    first = models.ForeignKey(model,on_delete=models.CASCADE,related_name="first")
    second = models.ForeignKey(model,on_delete=models.CASCADE,related_name="second")


    objects = threadmanager()   
    
    def __str__(self):
        return self.first.username + "-" + self.second.username

我的聊天消费者是

from channels.consumer import AsyncConsumer
import asyncio
from channels.db import database_sync_to_async
import json
from accounts.models import thread

from django.contrib.auth import get_user_model

model = get_user_model()




class ChatConsumer(AsyncConsumer):

    async def websocket_connect(self,event):

        await self.send(
            {
                'type':'websocket.accept',
            }
        )
        print("connected",event)

    async def websocket_receive(self,event):
        print("received",event)
        firstuser,seconduser = await self.get_socket_users(event)
        currentthread = await self.get_current_thread(firstuser,seconduser)
        print(currentthread)
    async def websocket_disconnect(self,event):
        print("disconnected",event)        

    @database_sync_to_async
    def get_socket_users(self,event):

        first = self.scope['user']
        second = model.objects.get(username=event['text'])
        return first,second

    @database_sync_to_async
    def get_current_thread(self,firstuser,seconduser):

        return  thread.objects.get_or_create(firstuser,seconduser)

每当我尝试在 2 个用户之间创建一个新线程时,我都会成功获得它:例如,我的第一个用户是 root,第二个用户是 rusker。

WebSocket HANDSHAKING /profile [127.0.0.1:35432]
WebSocket CONNECT /profile [127.0.0.1:35432]
connected {'type': 'websocket.connect'}
received {'type': 'websocket.receive', 'text': 'rusker'}
root-rusker

但是,每当我试图在相同的 2 个用户之间获取相同的线程时,我都会从数据库中接收线程,但是在返回线程期间,我会收到如下错误:

WebSocket CONNECT /profile [127.0.0.1:35432]
connected {'type': 'websocket.connect'}
received {'type': 'websocket.receive', 'text': 'rusker'}
root-rusker
received {'type': 'websocket.receive', 'text': 'rusker'}
root-rusker
Exception inside application: You cannot call this from an async context - use a thread or sync_to_async.
Traceback (most recent call last):
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 173, in __get__
    rel_obj = self.field.get_cached_value(instance)
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/django/db/models/fields/mixins.py", line 15, in get_cached_value
    return instance._state.fields_cache[cache_name]
KeyError: 'first'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/channels/sessions.py", line 183, in __call__
    return await self.inner(receive, self.send)
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/channels/middleware.py", line 41, in coroutine_call
    await inner_instance(receive, send)
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/channels/consumer.py", line 62, in __call__
    await await_many_dispatch([receive], self.dispatch)
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/channels/utils.py", line 51, in await_many_dispatch
    await dispatch(result)
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/channels/consumer.py", line 73, in dispatch
    await handler(message)
  File "/root/Desktop/pydjango/django-project/Game/Game/consumer.py", line 28, in websocket_receive
    print(currentthread)
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/django/db/models/query.py", line 266, in __repr__
    return '<%s %r>' % (self.__class__.__name__, data)
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/django/db/models/base.py", line 519, in __repr__
    return '<%s: %s>' % (self.__class__.__name__, self)
  File "/root/Desktop/pydjango/django-project/Game/accounts/models.py", line 42, in __str__
    return self.first.username + "-" + self.second.username
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 187, in __get__
    rel_obj = self.get_object(instance)
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 154, in get_object
    return qs.get(self.field.get_reverse_related_filter(instance))
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/django/db/models/query.py", line 425, in get
    num = len(clone)
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/django/db/models/query.py", line 269, in __len__
    self._fetch_all()
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/django/db/models/query.py", line 1308, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/django/db/models/query.py", line 53, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "/root/Desktop/pydjango/tenv/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1154, in execute_sql
    cursor = self.connection.cursor()
  File "/root/Desktop/pydjango/tenv/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 /profile [127.0.0.1:35432]

标签: pythondjangodjango-channels

解决方案


推荐阅读