Django Оценить кверисет отношений ManyToMany раньше времени для использования в async-функции
Я использую потребитель Django-Channels для асинхронного взаимодействия через websocket.
У меня есть что-то вроде этого ниже
class Command(UUIDModel):
owner = models.ForeignKey(AUTH_USER_MODEL, default=None, on_delete=models.SET_DEFAULT, null=True, blank=True, related_name='commands')
name = models.CharField('Name', default='New Command', max_length=128, blank=True, null=True)
class Secret(UUIDModel):
owner = models.ForeignKey(AUTH_USER_MODEL, default=None, on_delete=models.SET_DEFAULT, null=True, blank=True, related_name='secrets')
command = models.ManyToManyField(Command, blank=True, related_name='secrets')
@sync_to_async
def get_command(pk):
command = Command.objects.get(id=pk)
return command
class CommandConsumer(AsyncWebsocketConsumer):
@log_exceptions
async def command(self, event):
log.debug(event)
command = await get_command(event.get('command').get('id'))
log.debug(command)
log.debug(command.secrets)
log.debug(command.secrets.all()) # Fails here
return
При выполнении этой операции я получаю ошибку SynchronousOnlyOperation, как раз когда она оценивает набор запросов для Secrets в поле ManyToMany.
Есть ли способ заставить кверисет оцениваться заранее в синхронной функции get_command
, а не в async websocket? Таким образом я смогу легко получить доступ к секретам через command.secrets
.
В настоящее время мой обходной путь - просто обрабатывать секреты как отдельную переменную
@sync_to_async
def get_command(pk):
command = Command.objects.get(id=pk)
secrets = list(command.secrets.all())
return command, secrets
Использовать prefetch_related
https://docs.djangoproject.com/en/3.2/ref/models/querysets/#prefetch-related
@sync_to_async
def get_command(pk):
command = Command.objects.prefetch_related("secrets").get(id=pk)
return command
class CommandConsumer(AsyncWebsocketConsumer):
@log_exceptions
async def command(self, event):
log.debug(event)
command = await get_command(event.get('command').get('id'))
log.debug(command)
log.debug(command.secrets)
log.debug(command.secrets.all()) # No longer fails
return