Как эффективно загрузить большое количество данных из файла в таблицу Django
Что проверяют
Понимаете ли вы, где у загрузки узкое место: построчный save(), лишние запросы, память, транзакции. Хороший ответ идёт ступенями от bulk_create к COPY. Следом спрашивают, как обновлять уже существующие строки и что делать с невалидными.
Как отвечать
- читайте файл потоком:
csv.DictReaderпо открытому файлу, пачки черезitertools.isliceилиitertools.batchedв Python 3.12+. Весь файл в список не собирайте; bulk_create(objs, batch_size=...): одна вставка на пачку вместо запроса на каждую строку, пачки от 1000 до 5000 строк обычно хватает. Ограничения: не вызываетsave(), не шлёт сигналыpre_save/post_save, не работает с многотабличным наследованием;- upsert: с Django 4.1 у
bulk_createестьupdate_conflicts=Trueвместе сunique_fieldsиupdate_fields, аignore_conflicts=Trueпросто пропускает дубли; - типы и связи: строки из CSV приводите к типам сами, внешний ключ задавайте через
author_id, а не объектом, иначе на каждую строку уйдёт запрос за связанной записью; - транзакции:
transaction.atomic()на пачку. Одна транзакция на миллион строк долго держит блокировки и не даёт продолжить после сбоя; COPY: для миллионов строк в PostgreSQL он в разы быстрее любыхINSERT. В psycopg 3 это метод курсораcopy()и запись строк черезwrite_row(). Частый приём:COPYво временную таблицу, затемINSERT ... SELECT ... ON CONFLICTв основную.
Загрузку файла от пользователя запускайте не в запросе, а в фоне, через management command или задачу Celery, иначе упрётесь в таймаут воркера.
Где спотыкаются
Запускают импорт с DEBUG = True. Django складывает каждый запрос в connection.queries, и долгая загрузка съедает всю память процесса.
Копят в памяти все объекты модели перед одним вызовом bulk_create. На файле в несколько гигабайт процесс падает раньше, чем дойдёт до базы.
Не решают, что делать с плохими строками. Валидация модели при массовой вставке не выполняется, и одна кривая дата роняет пачку целиком. Невалидные строки отсеивают до вставки и пишут в отчёт с номером строки файла.