Исключение SynchronousOnlyOperation

Исключение SynchronousOnlyOperation возникает в Django, когда вы пытаетесь выполнить синхронную операцию, такую как обращение к базе данных или работа с ORM, в асинхронном контексте. Это исключение наследуется от класса RuntimeError и появляется при попытке выполнить синхронный код внутри асинхронной функции или при использовании асинхронных представлений. Основная причина - Django по умолчанию работает в синхронном режиме, и для асинхронной работы требуются специальные подходы.

Синтаксис

python# Исключение возникает при вызове синхронного кода из асинхронного
# Пример синхронного вызова в асинхронной функции
async def my_async_view(request):
  # Это вызовет SynchronousOnlyOperation
  users = User.objects.all()
  return JsonResponse({"users": list(users)})

Пример

Рассмотрим простой пример асинхронного представления, которое пытается получить данные из базы данных синхронным способом:

pythonfrom django.http import JsonResponse
from myapp.models import Article

async def async_articles_list(request):
  # Это вызовет SynchronousOnlyOperation
  articles = Article.objects.all()
  
  data = []
  for article in articles:
    data.append({
      "title": article.title,
      "published_date": article.published_date
    })
  
  return JsonResponse({"articles": data})

При выполнении этого кода вы получите следующую ошибку:

python"SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async."

Пример

Для решения проблемы нужно использовать обертку sync_to_async, которая позволяет выполнить синхронный код в асинхронном контексте:

pythonfrom django.http import JsonResponse
from asgiref.sync import sync_to_async
from myapp.models import Article

async def async_articles_list(request):
  # Используем sync_to_async для синхронного запроса
  @sync_to_async
  def get_articles():
    return list(Article.objects.all())
  
  articles = await get_articles()
  
  data = []
  for article in articles:
    data.append({
      "title": article.title,
      "published_date": article.published_date
    })
  
  return JsonResponse({"articles": data})

Пример

Также можно использовать sync_to_async в качестве декоратора для асинхронных функций:

pythonfrom django.http import JsonResponse
from asgiref.sync import sync_to_async
from myapp.models import User, Article

async def get_user_articles(user_id):
  @sync_to_async
  def get_user():
    return User.objects.get(id=user_id)
  
  @sync_to_async
  def get_articles(user):
    return list(Article.objects.filter(author=user))
  
  user = await get_user()
  articles = await get_articles(user)
  
  return articles

async def user_articles_view(request, user_id):
  articles = await get_user_articles(user_id)
  
  data = []
  for article in articles:
    data.append({
      "title": article.title,
      "author": article.author.username
    })
  
  return JsonResponse({"articles": data})

Результат выполнения кода для пользователя с идентификатором 1:

python[{"title": "First article", "author": "user1"}, {"title": "Second article", "author": "user1"}]

Смотрите также

  • исключение AppRegistryNotReady,
    которое возникает при обращении к моделям до их загрузки
  • исключение SystemCheckError,
    которое возникает при ошибках системной проверки
  • исключение DatabaseError,
    которое является базовым для ошибок базы данных
  • исключение CommandError,
    которое возникает при ошибках в пользовательских командах