在异步函数中从数据库中抓取模型(Channel Consumer)Django
Grabbing models from database in asynchronous function (Channel Consumer) Django
总结
您好,目前我正在开发一个应用程序,它通过使用 django-channels 将数据实时发送到前端。我已经能够对页面打开时创建的所有模型执行此操作,但是我还无法在页面尚未打开时获取以前的模型。
代码
下面是我第一次尝试让这个系统正常工作,当消费者连接方法被触发时会调用它,在连接被接受后我会 运行 这个 for 循环
for app in Application.objects.all():
app_json = ApplicationSerializer(app).data
await self.send_json({'action': 'create', 'data': app_json})
当运行使用这段代码时,我会得到错误信息
django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
这是有道理的,因为消费者连接方法是异步的,所以按照消息的建议我继续使用 sync_to_async
for app in sync_to_async(Application.objects.all)():
app_json = ApplicationSerializer(app).data
await self.send_json({'action': 'create', 'data': app_json})
我决定在 Application 对象周围使用 sync_to_async
,因为错误消息突出显示了 for 循环行本身,而且我知道 applicationSerializer 可以正常工作,因为我已经在其他异步方法中使用它了
这导致 TypeError: 'coroutine' object is not iterable
最后的想法
我对 django-channels 和异步都是新手,如果我知道通道将在很大程度上基于异步,我会在开始这个项目之前研究这个系统。
我查看了其他堆栈溢出帖子,发现潜在映射数据可能是一种解决方案,但我不能 100% 确定这是异步问题还是 django-channels
还有哪些我可以尝试的东西?
更新
根据 Kens 的建议,我继续尝试了以下代码:
apps = await database_sync_to_async(Application.objects.all)()
for app in apps:
app_json = ApplicationSerializer(app).data
print(app_json)
这返回了堆栈跟踪
Exception inside application: You cannot call this from an async context - use a thread or sync_to_async.
Traceback (most recent call last):
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\staticfiles.py", line 44, in __call__
return await self.application(scope, receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\routing.py", line 71, in __call__
return await application(scope, receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\sessions.py", line 47, in __call__
return await self.inner(dict(scope, cookies=cookies), receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\sessions.py", line 254, in __call__
return await self.inner(wrapper.scope, receive, wrapper.send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\auth.py", line 181, in __call__
return await super().__call__(scope, receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\middleware.py", line 26, in __call__
return await self.inner(scope, receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\routing.py", line 150, in __call__
return await application(
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\consumer.py", line 94, in app
return await consumer(scope, receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\consumer.py", line 58, in __call__
await await_many_dispatch(
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\utils.py", line 51, in await_many_dispatch
await dispatch(result)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\consumer.py", line 73, in dispatch
await handler(message)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\generic\websocket.py", line 196, in websocket_receive
await self.receive(text_data=message["text"])
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\generic\websocket.py", line 259, in receive
await self.receive_json(await self.decode_json(text_data), **kwargs)
File "D:\HomeAutomation\HomeAutomation\consumers.py", line 28, in receive_json
for app in apps:
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\django\db\models\query.py", line 287, in __iter__
self._fetch_all()
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\django\db\models\query.py", line 1308, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\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 "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\django\db\models\sql\compiler.py", line 1154, in execute_sql
cursor = self.connection.cursor()
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\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.
这段代码sync_to_async(Application.objects.all)()
创建了一个协程,需要等待,不能直接使用。对于您的具体情况,最好先在 for 循环之外获取数据,然后再继续。另外,您可能想改用 database_sync_to_async
。
from channels.db import database_sync_to_async
apps = await database_sync_to_async(Application.objects.all)()
for app in apps:
app_json = ApplicationSerializer(app).data
await self.send_json({'action': 'create', 'data': app_json})
附带说明一下,确保在序列化程序序列化应用程序实例时不会有数据库查询。为此,请使用 select_related
和 prefetch_related
预取可能需要的任何嵌套对象
UPDATE:是的,由于某些 Django ORM 方法的惰性,例如 .all()
,在调用方法,但在使用它的时候。强制它立即执行的一种常见方法是将查询集转换为列表。
试试这个:
from channels.db import database_sync_to_async
apps = await database_sync_to_async(list)(Application.objects.all())
for app in apps:
app_json = ApplicationSerializer(app).data
await self.send_json({'action': 'create', 'data': app_json})
总结
您好,目前我正在开发一个应用程序,它通过使用 django-channels 将数据实时发送到前端。我已经能够对页面打开时创建的所有模型执行此操作,但是我还无法在页面尚未打开时获取以前的模型。
代码
下面是我第一次尝试让这个系统正常工作,当消费者连接方法被触发时会调用它,在连接被接受后我会 运行 这个 for 循环
for app in Application.objects.all():
app_json = ApplicationSerializer(app).data
await self.send_json({'action': 'create', 'data': app_json})
当运行使用这段代码时,我会得到错误信息
django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async.
这是有道理的,因为消费者连接方法是异步的,所以按照消息的建议我继续使用 sync_to_async
for app in sync_to_async(Application.objects.all)():
app_json = ApplicationSerializer(app).data
await self.send_json({'action': 'create', 'data': app_json})
我决定在 Application 对象周围使用 sync_to_async
,因为错误消息突出显示了 for 循环行本身,而且我知道 applicationSerializer 可以正常工作,因为我已经在其他异步方法中使用它了
这导致 TypeError: 'coroutine' object is not iterable
最后的想法
我对 django-channels 和异步都是新手,如果我知道通道将在很大程度上基于异步,我会在开始这个项目之前研究这个系统。
我查看了其他堆栈溢出帖子,发现潜在映射数据可能是一种解决方案,但我不能 100% 确定这是异步问题还是 django-channels
还有哪些我可以尝试的东西?
更新
根据 Kens 的建议,我继续尝试了以下代码:
apps = await database_sync_to_async(Application.objects.all)()
for app in apps:
app_json = ApplicationSerializer(app).data
print(app_json)
这返回了堆栈跟踪
Exception inside application: You cannot call this from an async context - use a thread or sync_to_async.
Traceback (most recent call last):
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\staticfiles.py", line 44, in __call__
return await self.application(scope, receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\routing.py", line 71, in __call__
return await application(scope, receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\sessions.py", line 47, in __call__
return await self.inner(dict(scope, cookies=cookies), receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\sessions.py", line 254, in __call__
return await self.inner(wrapper.scope, receive, wrapper.send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\auth.py", line 181, in __call__
return await super().__call__(scope, receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\middleware.py", line 26, in __call__
return await self.inner(scope, receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\routing.py", line 150, in __call__
return await application(
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\consumer.py", line 94, in app
return await consumer(scope, receive, send)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\consumer.py", line 58, in __call__
await await_many_dispatch(
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\utils.py", line 51, in await_many_dispatch
await dispatch(result)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\consumer.py", line 73, in dispatch
await handler(message)
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\generic\websocket.py", line 196, in websocket_receive
await self.receive(text_data=message["text"])
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\channels\generic\websocket.py", line 259, in receive
await self.receive_json(await self.decode_json(text_data), **kwargs)
File "D:\HomeAutomation\HomeAutomation\consumers.py", line 28, in receive_json
for app in apps:
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\django\db\models\query.py", line 287, in __iter__
self._fetch_all()
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\django\db\models\query.py", line 1308, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\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 "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\site-packages\django\db\models\sql\compiler.py", line 1154, in execute_sql
cursor = self.connection.cursor()
File "C:\Users\Devin\.virtualenvs\HomeAutomation-l9uhKhE0\lib\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.
这段代码sync_to_async(Application.objects.all)()
创建了一个协程,需要等待,不能直接使用。对于您的具体情况,最好先在 for 循环之外获取数据,然后再继续。另外,您可能想改用 database_sync_to_async
。
from channels.db import database_sync_to_async
apps = await database_sync_to_async(Application.objects.all)()
for app in apps:
app_json = ApplicationSerializer(app).data
await self.send_json({'action': 'create', 'data': app_json})
附带说明一下,确保在序列化程序序列化应用程序实例时不会有数据库查询。为此,请使用 select_related
和 prefetch_related
预取可能需要的任何嵌套对象
UPDATE:是的,由于某些 Django ORM 方法的惰性,例如 .all()
,在调用方法,但在使用它的时候。强制它立即执行的一种常见方法是将查询集转换为列表。
试试这个:
from channels.db import database_sync_to_async
apps = await database_sync_to_async(list)(Application.objects.all())
for app in apps:
app_json = ApplicationSerializer(app).data
await self.send_json({'action': 'create', 'data': app_json})