Захват моделей из базы данных в асинхронной функции (потребитель канала) Django

#python #django #asynchronous #django-channels

#питон #джанго #асинхронный #django-каналы

Вопрос:

Краткие сведения

Здравствуйте, в настоящее время я работаю над приложением, которое отправляет данные на интерфейс в режиме реального времени с помощью django-каналов. Я смог сделать это для всех моделей, которые создаются при открытии их страницы, однако я еще не смог захватить предыдущие модели, когда страница еще не была открыта.

Код

Ниже была одна из моих первых попыток заставить эту систему работать, это будет вызвано, когда будет запущен метод подключения потребителя, и после того, как соединение было принято, я бы запустил этот цикл 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})
 

Я решил использовать sync_to_async объекты приложения, поскольку в сообщении об ошибке была выделена сама строка цикла for, а также я знаю, что applicationSerializer будет работать правильно, поскольку я использовал его в других асинхронных методах просто отлично

Это приводит к TypeError: 'coroutine' object is not iterable

Заключительные мысли

Я новичок как в django-каналах, так и в асинхронных, если бы я знал, что каналы будут в значительной степени основаны на асинхронных, я бы изучил систему до начала этого проекта.

Я просмотрел другие сообщения о переполнении стека и увидел, что потенциально отображение данных может быть решением, но я не уверен на 100%, является ли это асинхронной проблемой или с django-каналами

Какие еще вещи я могу попробовать?

Обновить

Основываясь на предложении 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:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelsstaticfiles.py", line 44, in __call__
    return await self.application(scope, receive, send)
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelsrouting.py", line 71, in __call__
    return await application(scope, receive, send)
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelssessions.py", line 47, in __call__
    return await self.inner(dict(scope, cookies=cookies), receive, send)
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelssessions.py", line 254, in __call__
    return await self.inner(wrapper.scope, receive, wrapper.send)
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelsauth.py", line 181, in __call__
    return await super().__call__(scope, receive, send)
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelsmiddleware.py", line 26, in __call__
    return await self.inner(scope, receive, send)
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelsrouting.py", line 150, in __call__
    return await application(
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelsconsumer.py", line 94, in app
    return await consumer(scope, receive, send)
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelsconsumer.py", line 58, in __call__
    await await_many_dispatch(
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelsutils.py", line 51, in await_many_dispatch
    await dispatch(result)
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelsconsumer.py", line 73, in dispatch
    await handler(message)
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelsgenericwebsocket.py", line 196, in websocket_receive
    await self.receive(text_data=message["text"])
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packageschannelsgenericwebsocket.py", line 259, in receive
    await self.receive_json(await self.decode_json(text_data), **kwargs)
  File "D:HomeAutomationHomeAutomationconsumers.py", line 28, in receive_json
    for app in apps:
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packagesdjangodbmodelsquery.py", line 287, in __iter__
    self._fetch_all()
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packagesdjangodbmodelsquery.py", line 1308, in _fetch_all
    self._result_cache = list(self._iterable_class(self))
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packagesdjangodbmodelsquery.py", line 53, in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packagesdjangodbmodelssqlcompiler.py", line 1154, in execute_sql
    cursor = self.connection.cursor()
  File "C:UsersDevin.virtualenvsHomeAutomation-l9uhKhE0libsite-packagesdjangoutilsasyncio.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.
 

Ответ №1:

Этот код 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 для предварительной выборки любых вложенных объектов, которые могут потребоваться

ОБНОВЛЕНИЕ: Да, из-за ленивой природы некоторых методов 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})
 

Комментарии:

1. Здравствуйте, я пошел дальше и попробовал то, что вы предложили, и, что интересно, я получил ту же ошибку django.core.exceptions.SynchronousOnlyOperation: You cannot call this from an async context - use a thread or sync_to_async. , что и там ошибка на этот раз возникает, когда мы объявляем apps

2. @Timberghost_ опубликуйте полную трассировку стека

3. Спасибо за быстрый ответ, я пошел дальше и обновил сообщение с помощью stacktrace

4. @Timberghost_ да, виноват. Проверьте мое редактирование

5. я рад, что смог быть вам полезен. Это не обязательно должен быть конкретный список, но это то, что обычно используется, потому что список похож на queryset, и вы все равно можете перебирать его. По соображениям производительности и, возможно, по другим причинам, Django не выполняет запрос, он доходит до точки, где необходимы объекты. Чтобы преобразовать набор запросов в список Python, ему необходимо загрузить данные из базы данных, следовательно, выполнить запрос. Вы можете прочитать об этом в документации docs.djangoproject.com/en/3.1/topics/db/queries /.