Оптимизация массовых действий для миллионов записей
Действия в Django Admin обманчиво просты. Выбрал строки, выбрал действие, нажал «Выполнить». Но за этой простотой скрывается ловушка: дефолтная реализация загружает все выбранные объекты в память и обрабатывает их последовательно в одном цикле запрос-ответ.
Проблема на масштабе
Когда queryset содержит 100 000+ строк, дефолтное поведение вызывает:
- Исчерпание памяти — Django загружает все объекты в Python
- Таймауты запросов — Nginx/Gunicorn убивает долгие запросы
- Блокировки БД — массовые обновления могут заблокировать таблицы
Чанкированная обработка
Первая оптимизация — обработка пакетами.
from django.contrib import admin
@admin.action(description="Архивировать выбранные")
def archive_items(modeladmin, request, queryset):
CHUNK_SIZE = 500
total = queryset.count()
processed = 0
while processed < total:
chunk_ids = list(
queryset.values_list('id', flat=True)[processed:processed + CHUNK_SIZE]
)
queryset.model.objects.filter(id__in=chunk_ids).update(
status='archived',
archived_at=timezone.now()
)
processed += CHUNK_SIZE
modeladmin.message_user(request, f"Архивировано {total} записей.")
Делегирование в Celery
Для действительно больших датасетов действие должно только поставить задачу в очередь:
@admin.action(description="Экспорт в CSV (асинхронно)")
def export_csv_async(modeladmin, request, queryset):
task = export_csv_task.delay(
model=queryset.model.__name__,
ids=list(queryset.values_list('id', flat=True)),
user_email=request.user.email
)
modeladmin.message_user(
request,
f"Экспорт поставлен в очередь (задача {task.id}). Результат придёт на email."
)
Ключевые выводы
- Никогда не обрабатывайте более ~1000 строк синхронно в admin action
- Используйте
.update()вместо итерации с.save()на каждом объекте - Для экспорта и отчётов всегда используйте очередь задач
forum Обсуждение
Комментарии скоро будут доступны. Следите за обновлениями!