Django Async View - Модель __str__

Я пытаюсь перевести мое существующее приложение Django 4.1 на async по причинам производительности. Это более сложная задача, чем я предполагал вначале.

Ниже приведен тестовый код:

async def diagrams(request):

    tests = await sync_to_async(list)(Test.objects.filter(name='Test 1'))
    print(tests)

    return render(request, 'analyticsApp/test.html')


class Test2(models.Model):
    name = models.CharField(max_length=50, default='', blank=True)

    def __str__(self):
        return self.name


class Test(models.Model):
    name = models.CharField(max_length=50, default='', blank=True)
    testForeignKey = models.ForeignKey(Test2, editable=True, on_delete=models.CASCADE, blank=True, null=True)

    def __str__(self):
        return self.name + '_' + self.testForeignKey.name

Итак, я вроде бы разобрался, как "фильтровать" объекты с помощью async_to_async. Однако проблема, которую я пытаюсь решить, заключается в использовании __str__ в модели. Все мои модели используют __str__, чтобы дать точное строковое представление модели. Похоже, что это невозможно сделать? Я пытался преобразовать def __str__ в async def __str__, но django не ожидает этого при вызове, поэтому возникают проблемы.

Есть идеи, как с этим справиться?

Возможно, асинхронность не увеличит вашу производительность.

В вашем коде вы работаете не корректно с async. Строка должна работать с существующими данными, без запроса к базе данных. Поэтому вы должны получить необходимые данные до инициализации экземпляра.

В вашем случае вы можете сделать:

obj = Test.objects.select_related("testForeignKey").get(**something)

Также вы можете использовать defer/only. Запрос работает async, после чего вы получаете свой Str(объект) нормально.

Я не очень часто использовал поддержку async, но в качестве обходного пути вы можете использовать select_related

async def diagrams(request):

    tests = await sync_to_async(list)(
        Test.objects.filter(name='Test 1')
            .select_related('testForeignKey')
    )
    print(tests)

    return render(request, 'analyticsApp/test.html')

Я думаю, что ваша проблема здесь в print(tests)

Тогда запрос оценивается, и в этот момент вызывается __str__ для печати списка тестов. Проще говоря, если вы не выведете их __str__ никогда не будет вызван в асинхронном контексте!

Кроме того, .filter просто подготавливает sql-запрос, и нет необходимости оборачивать его в sync_to_async, если только вам действительно не нужен список, который заставит его оценить. В данном примере он не нужен, поскольку вы можете передать набор запросов непосредственно в ваш шаблон для рендеринга, и он будет оценен там. Вы можете просто сделать следующее:

tests = Test.objects.filter(name='Test 1')
return render(request, 'analyticsApp/test.html', { 'tests':tests })

Если вам абсолютно необходимо вывести их на печать, то просто оберните печать в sync_to_async

await sync_to_async(print)(tests)

Или если вам нужно вывести их в виде списка, тогда

test_list = await sync_to_async(list)(tests)
await sync_to_async(print)(test_list)

Print необходимо синхронизировать, чтобы получить доступ к нотации . для доступа к связанным полям. То же самое касается использования точечной нотации для доступа к связанным полям в вашем шаблоне. Если это так, то оберните рендеринг в sync_to_async и ожидайте его.

return await sync_to_async(render)(request, 'analyticsApp/test.html', { 'tests':tests })

Все, что вы делаете с помощью select_related, это заставляете оценку связанных полей происходить одновременно с созданием списка в контексте синхронизации, и именно поэтому она работает с точечной нотацией в вашем шаблоне или с print.

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