使用Django Channels异步Consumer执行ORM查询报错求助
Django Channels异步Consumer中ORM查询报错解决方法
问题场景
使用Django Channels实现WebSocket功能,在consumers.py中通过ORM执行数据库查询,代码如下:
import json from channels.generic.websocket import AsyncWebsocketConsumer from .models import Game from accounts.models import CustomUser as User from channels.db import database_sync_to_async class CameraStreamConsumer(AsyncWebsocketConsumer): def __init__(self, *args, **kwargs): print("initializing") self.counter = 0 super().__init__(*args, **kwargs) async def connect(self): await self.accept() print("accepted connection") async def disconnect(self, close_code): pass print("disconect connection") async def receive(self, text_data=None,): print(" iam getting data online") data = json.loads(text_data) if data.get('type') == 'started': await self.handle_game_started(data) async def get_game(self, game_id): game_obj = await database_sync_to_async(Game.objects.get)(id=game_id) return game_obj async def get_user(self, user_id): user_obj = await database_sync_to_async(User.objects.get)(id=user_id) return user_obj async def handle_game_started(self, data): game_id = data.get('game_id') user_id = data.get('user_id') game_obj = await self.get_game(game_id) user_obj = await self.get_user(user_id) if game_obj and user_obj: game_creator = game_obj.creator if game_creator == user_obj: print("user is creator") await self.send(text_data=json.dumps({'message': f'Game {game_id} Started'}))
运行后触发错误:
django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
尝试导入from asgiref.sync import sync_to_async处理后,仍出现相同错误。
问题根源
虽然用database_sync_to_async包装了Game.objects.get,但访问game_obj.creator时,这是一个同步的关联数据库查询(Django ORM默认延迟加载关联对象),该操作未被异步包装,因此在异步上下文里触发报错。
解决方案
方案1:预取关联数据(推荐)
在获取Game对象时,使用select_related预取creator字段,避免后续访问时触发同步查询:
async def get_game(self, game_id): # 用select_related提前加载creator关联对象 game_obj = await database_sync_to_async(Game.objects.select_related('creator').get)(id=game_id) return game_obj
方案2:包装关联对象的访问操作
如果不需要预取,可将访问game_obj.creator的操作也包装在database_sync_to_async中:
async def handle_game_started(self, data): game_id = data.get('game_id') user_id = data.get('user_id') game_obj = await self.get_game(game_id) user_obj = await self.get_user(user_id) if game_obj and user_obj: # 把获取creator的操作转为异步 game_creator = await database_sync_to_async(lambda: game_obj.creator)() if game_creator == user_obj: print("user is creator") await self.send(text_data=json.dumps({'message': f'Game {game_id} Started'}))
注意事项
- 所有涉及数据库的操作(包括关联字段访问、自定义模型方法调用)都必须被
database_sync_to_async或sync_to_async包装,或者通过ORM预取方法提前加载数据。 - 对于多对多关联,使用
prefetch_related替代select_related进行预取。
内容的提问来源于stack exchange,提问作者Simple Sarfi
相关产品推荐
相关产品推荐

