Fix Django SynchronousOnlyOperation in Async Code
Fix Django SynchronousOnlyOperation You cannot call this from an async context: use async ORM methods, sync_to_async for blocking code, avoid lazy relation access in async views and consumers, and never disable the check in production.
Problem statement
An async view, a Channels consumer, or code run under Uvicorn raises:
django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async
context - use a thread or sync_to_async.
Django's ORM and many other parts of Django are synchronous: they block while waiting on the database. Calling them directly inside an async def function would block the event loop and freeze every other request on that worker, so Django detects it and raises this error instead. The fix is to use the async ORM API where it exists and to run remaining blocking code in a thread with sync_to_async.
- Use async ORM methods:
await Model.objects.aget(...),acreate,asave,adelete,async for obj in qs. - Wrap blocking code that has no async version:
await sync_to_async(func)(args). - Avoid lazy access to related objects in async code (
order.customer.name); useselect_relatedoragetthe relation. - In Channels, use
database_sync_to_asyncfor ORM work in consumers. - Never set
DJANGO_ALLOW_ASYNC_UNSAFEin production.
Step-by-step solution
1. Read the traceback to find the sync call
The last frames show which call triggered the guard: QuerySet.__iter__, Model.save, connection.cursor, django.contrib.auth.get_user, or a template rendering a queryset. That call must become async or move to a thread.
2. Use the async ORM
Since Django 4.1, querysets and model instances have async versions of their I/O methods:
async def order_detail(request, pk):
order = await Order.objects.select_related("customer").aget(pk=pk)
lines = [line async for line in order.lines.all()]
count = await Order.objects.filter(customer=order.customer).acount()
return JsonResponse({"customer": order.customer.name, "lines": len(lines), "orders": count})
Building a queryset is fine in async code because it does not touch the database; only evaluating it does. aget, afirst, acount, aexists, acreate, aupdate, adelete, async for, and instance.asave() evaluate safely.
3. Watch for lazy relation access
This looks harmless and raises the error:
order = await Order.objects.aget(pk=pk)
name = order.customer.name # lazy foreign key fetch: sync query
Either fetch the relation up front with select_related("customer"), or fetch it asynchronously: customer = await Customer.objects.aget(pk=order.customer_id). The same applies to reverse relations and ManyToMany managers, and to request.user in async views: use await request.auser() (Django 5.0+).
4. Wrap code without async support
Some things have no async API: Model.save() overrides with custom logic, third-party libraries, send_mail, transactions, and much of django.contrib. Run them in a thread:
from asgiref.sync import sync_to_async
from django.db import transaction
@sync_to_async
def place_order(cart_id):
with transaction.atomic():
...
return order.pk
async def checkout(request):
order_id = await place_order(request.POST["cart"])
return redirect("order-done", order_id)
sync_to_async runs the function in a thread with thread_sensitive=True by default, which keeps Django's database connection handling correct. Group several ORM operations into one wrapped function rather than wrapping each call; it is faster and keeps transactions together.
5. Channels consumers
from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncJsonWebsocketConsumer
class ChatConsumer(AsyncJsonWebsocketConsumer):
async def receive_json(self, content):
msg = await self.save_message(content["text"])
await self.channel_layer.group_send(self.room, {"type": "chat.message", "id": msg})
@database_sync_to_async
def save_message(self, text):
return Message.objects.create(room=self.room, text=text).pk
database_sync_to_async also closes stale connections around the call, which matters for long-lived WebSocket connections.
Verification check: run the view or consumer under the ASGI server you deploy with (uvicorn config.asgi:application) and exercise it; the error should not appear. Add an async test with django.test.AsyncClient that calls the view, so regressions fail in CI.
Explanation
An async server runs many requests on one thread, switching between them whenever one awaits I/O. A synchronous database call does not await; it holds the thread until the query returns, stalling every other request. Django marks async-unsafe operations with a decorator that checks whether an event loop is running in the current thread and raises SynchronousOnlyOperation if so. The async ORM methods currently run the underlying sync code in a thread pool for you; native async database drivers are being added gradually.
Edge cases / notes
DJANGO_ALLOW_ASYNC_UNSAFEdisables the check. It exists for notebooks and shells that run an event loop, not for servers.- Jupyter and IPython run an event loop, so the ORM raises this in notebooks; setting the variable there is acceptable.
- Sync views under ASGI are fine: Django runs them in a thread automatically. You only need async care inside
async def. - Performance. An async view that wraps everything in
sync_to_asyncis slower than a plain sync view. Use async where it gives concurrency benefits, such as calling external APIs or streaming.
Next steps
- Decide if async is worth it with Django WSGI vs ASGI.
- Run it in production with Deploy Django With Uvicorn and Nginx.
FAQ
Can I use the Django ORM in async views?
Yes, through the async methods (aget, acreate, async for) or by wrapping sync code with sync_to_async.
Is it safe to set DJANGO_ALLOW_ASYNC_UNSAFE?
Not in production. It lets blocking calls freeze the event loop for every request on that worker.
Why does accessing a foreign key raise the error?
Accessing an unloaded relation runs a synchronous query. Load it with select_related or fetch it asynchronously.
Do sync views break under Uvicorn?
No. Django runs sync views in a thread under ASGI. The error only affects code inside async functions.