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.