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

Вернуться на верх