Django:SynchronousOnlyOperation:您不能从异步上下文调用它 - 使用线程或 sync_to_async

Reg*_*lva 23 asynchronous jupyter django-3.0

我使用 Django 3.0.6 和运行shell_plus --notebook.

我尝试运行查询集:

User.objects.all()

但是返回这个错误SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async

我试试这个命令

from asgiref.sync import sync_to_async

users = sync_to_async(User.objects.all())

for user in users:
    print(user)

TypeError: 'SyncToAsync' object is not iterable
Run Code Online (Sandbox Code Playgroud)

Django文档的解决方案

os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true" 在 settings.py 中是唯一的解决方案吗?

Tom*_*ick 21

sync_to_async takes a callable, not the result. Instead, you want this:

from asgiref.sync import sync_to_async

users = sync_to_async(User.objects.all)()

for user in users:
    print(user)
Run Code Online (Sandbox Code Playgroud)

You can also put the call(s) you want to wrap in a decorated function:

from asgiref.sync import sync_to_async

@sync_to_async
def get_all_users():
    return User.objects.all()

for user in await get_all_users():
    print(user)
Run Code Online (Sandbox Code Playgroud)

Note that this must be used from an async context, so a full example would look like:

from asgiref.sync import sync_to_async

@sync_to_async
def get_all_users():
    return User.objects.all()

async def foo(request):
    for user in await get_all_users():
        print(user)
Run Code Online (Sandbox Code Playgroud)

Full documentation

  • 返回此错误。`<ipython-input-8-ade7a982ccf1>:1: RuntimeWarning: 协程 'SyncToAsync.__call__' 从未在 get_all_users() 中等待用户: RuntimeWarning: 启用 Tracemalloc 以获取对象分配回溯 --------- -------------------------------------------------- ---------------- TypeError Traceback (最近一次调用) <module> 中的 <ipython-input-8-ade7a982ccf1> ----> get_all_users() 中的用户为 1: 2 print(user) TypeError: 'coroutine' 对象不可迭代` (3认同)
  • 您的代码 `(for user in ...` 需要位于异步函数内。您可能还需要等待它,因为您要将其转换为协程,例如 `users = wait get_all_users()` (2认同)

lyh*_*543 18

出现错误是因为 Jupyter notebooks 有一个正在运行的事件循环。从文档

如果您尝试从存在正在运行的事件循环的线程中运行这些部分中的任何一个,您将收到 SynchronousOnlyOperation 错误。请注意,您不必直接在异步函数内即可发生此错误。如果您直接从异步函数调用同步函数,而没有通过诸如 sync_to_async() 或线程池之类的方法,那么它也可能发生,因为您的代码仍在异步上下文中运行。

如果遇到此错误,则应修复代码以不从异步上下文调用有问题的代码;相反,在它自己的同步函数中编写与 async-unsafe 对话的代码,并使用 asgiref.sync.sync_to_async() 或任何其他在其自己的线程中运行同步代码的首选方式来调用它。

如果您绝对迫切需要从异步上下文中运行此代码 - 例如,它是由外部环境强加给您的,并且您确定它不可能同时运行(例如您在 Jupyter 中) notebook),然后您可以使用 DJANGO_ALLOW_ASYNC_UNSAFE 环境变量禁用警告。

由于问题特定于 jupyter notebook 环境,因此以下是一个有效的解决方案。

import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'rest.settings')
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
django.setup()
    
from users.models import User
User.objects.all()
Run Code Online (Sandbox Code Playgroud)

但是需要谨慎,不要按照文档警告在生产环境中使用它。

  • 很简单,但是正如文档中指出的,这可能会导致数据损坏:“如果您不小心尝试从异步视图调用 Django 中仍仅同步的部分,您将触发 Django 的异步安全保护来保护您的数据来自腐败。” (3认同)

小智 14

您无法在同步和异步函数之间传递查询集,因为它是惰性的。您需要在异步上下文中评估它。

像这样:

from django.contrib.auth.models import User
from asgiref.sync import sync_to_async
import asyncio


@sync_to_async
def get_users():
    return list(
        User.objects.all()
    )

async def user_loop():
    results = await get_users()
    for r in results:
        print(r.username)


loop = asyncio.get_event_loop()
loop.run_until_complete(user_loop())
Run Code Online (Sandbox Code Playgroud)


Ehs*_*adi 6

文档

\n

如果您尝试运行 Django 的任何非异步部分(例如 ORM),您将收到 SynchronousOnlyOperation 错误。请注意,您不必直接位于异步函数内才能发生此错误。如果您直接从异步函数调用同步函数,而不使用sync_to_async()或类似的方法,那么也可能会发生这种情况。这是因为您的代码仍在具有活动事件循环的线程中运行,即使它可能未声明为异步代码。

\n

你应该使用:

\n
from asgiref.sync import sync_to_async\n\ndef _get_blog(pk):\n    return Blog.objects.get(pk=pk)\n\nget_blog = await sync_to_async(_get_blog, thread_sensitive=True)(10)\n\n....\n\n
Run Code Online (Sandbox Code Playgroud)\n

或者

\n
from asgiref.sync import sync_to_async\n\nget_blog = await sync_to_async(Blog.objects.get, thread_sensitive=True)(10)\n\n
Run Code Online (Sandbox Code Playgroud)\n

您可以使用Django通道的database_sync_to_async,它是asgiref.sync的改进的sync_to_async函数

\n

例如:

\n
from channels.db import database_sync_to_async\n\nget_blog = await database_sync_to_async(Blog.objects.get, thread_sensitive=True)(10)\n\n
Run Code Online (Sandbox Code Playgroud)\n