I use Django 3.0.6 and Jupyter notebook running with shell_plus --notebook
.
I try run queryset:
User.objects.all()
But return this error SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async
.
I try this command
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
The solution of Django documentation
os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"
in settings.py is the unique solution?
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)
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)
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)
Full documentation
The error occurs because Jupyter notebooks has a running event loop. From documentation
If you try to run any of these parts from a thread where there is a running event loop, you will get a SynchronousOnlyOperation error. Note that you don’t have to be inside an async function directly to have this error occur. If you have called a synchronous function directly from an asynchronous function without going through something like sync_to_async() or a threadpool, then it can also occur, as your code is still running in an asynchronous context.
If you encounter this error, you should fix your code to not call the offending code from an async context; instead, write your code that talks to async-unsafe in its own, synchronous function, and call that using asgiref.sync.sync_to_async(), or any other preferred way of running synchronous code in its own thread.
If you are absolutely in dire need to run this code from an asynchronous context - for example, it is being forced on you by an external environment, and you are sure there is no chance of it being run concurrently (e.g. you are in a Jupyter notebook), then you can disable the warning with the DJANGO_ALLOW_ASYNC_UNSAFE environment variable.
As the question is specific to jupyter notebook environment following is a valid solution.
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()
One need to however by wary and not use it in production environment as per documentation warnings.
You cant pass around a Queryset between sync and async functions since it is lazy. You need to evaluate it inside a async context.
Like this:
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())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With