Как решить проблему N+1 в Django для экспорта данных?
Я пытаюсь экспортировать данные в CSV и для этого мне нужно запросить тысячи экземпляров модели. Без каких-либо оптимизаций мой код может экспортировать ~700 экземпляров и связанные с ними статусы. При большем количестве сервер завершает работу с ошибкой 502 - Bad Gateway
.
Добавление prefetch_related('statuses')
улучшило производительность, но все равно происходит сбой при выборе более ~1000 экземпляров.
Статусы 200 и 300 могут отсутствовать или существовать несколько раз. В противном случае я мог бы отказаться от строки obj.statuses.filter(status=status_number).first()
и просто добавить их по порядку, но я думаю, что здесь это необходимо.
Как я могу улучшить производительность этого фрагмента кода? Или я неправильно использую prefetch_related()
?
# models.py
class MyExportModel(models.Model):
id = models.PrimaryKey()
name = models.CharField(max_length=255)
class StatusModel(models.Model):
STATUS_CHOICES = [
(100, 'created'),
(200, 'paid'),
(300, 'cancelled'),
(400, 'billed'),
(500, 'completed')
]
export_model = models.ForeignKey('MyExportModel', related_name='statuses', on_delete=models.CASCADE)
number = models.IntegerField(choices=STATUS_CHOICES)
created = models.DateTimeField(auto_now_add=True)
# export_as_csv.py
def export_as_csv(writer, queryset):
# Write headings into CSV
writer.writerow(['id', 'name'] + [f'{number} - {name}' for (number, name) in StatusModel.STATUS_CHOICES])
# Iterate over all selected models and their related statuses
for obj in queryset.prefetch_related('statuses'):
fields = [obj.id, obj.name]
# Append date for statuses if known (problematic section)
for (status_number, _) in StatusModel.STATUS_CHOICES:
status = obj.statuses.filter(status=status_number).first()
fields.append(status.created if status is not None else '')
# Write row for instance
writer.writerow(fields)