Если вы разработчик и выбрали для своего проекта Django Framework, вы уже знаете насколько это отличное решение. Проекту уже почти 20 лет. Кроме того, что сам проект хорошо документирован, в сети много статей и подробной документации по разным областям применения. Найти готовое решение, пример скрипта или ответ на свой вопрос довольно легко. Фреймворк с понятными парадигмами, разными «батарейками» в комплекте, а главное надежный. На нём можно строить почти любые по сложности сайты — от простых до SaaS решений.
Сегодня я хочу дать пару важных советов для тех, кому ещё предстоит делать большие миграции на своем Django проекте. Поверьте, вам они очень пригодятся, чтобы ваш сайт не пребывал в даунтайме несколько часов, а вы всё это время не находились в сложном эмоциональном состоянии.
Если тема миграций на Django для вас совсем новая, то на хабре есть хорошие подробные статьи которые раскрывают много деталей: раз, два. Советы в этой же статье выстраданы автором лично, применимы к PostgreSQL 14 и Django 5.2.7, но на самом деле они подходят для всех версий фреймворка.
Совет 1: перед миграцией обязательно проверяйте свободное место на диске
Для жирных таблиц размером гигабайты и десятки гигабайт обязательно нужно убедиться, что у вас достаточно свободного места. К примеру, для таблицы размером 10 Гб нужно столько же свободного места на диске, а лучше чуть больше. Лично я придерживаюсь правила держать диск на 60% свободным и больше.
А если вы подумали, что у вас очень простая ��играция. К примеру, которая меняет тип даных с Integer на Decimal и вам не требуется такое изобилие свободного места. Посмотрите на этот пример:
# Generated by Django 5.2.7 on 2026-02-22 09:57 from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("pages", "0006_alter_sigmaapi_report"), ] operations = [ migrations.AlterField( model_name="report", name="сumulative_layout_shift ", field=models.DecimalField( blank=True, decimal_places=4, default=0, max_digits=5, null=True, verbose_name="CLS", ), ), ]
Казалось бы, пример как пример. Довольно тривиальный по замыслу. В нём всего один ALTER на таблицу:
BEGIN; ALTER TABLE "report" ALTER COLUMN "сumulative_layout_shift" TYPE numeric(5, 4) USING "original_page_cls"::numeric(5, 4); COMMIT;
Но подвох в том, что в этом случае PostgreSQL переписывает таблицу целиком. Кроме этого запускается процесс перестраивания индексов, которые также съедят пространство на диске. И если вам в процессе не хватит диска, вам потребуется дополнительное время на поиск срочных решений, когда миграция рухнет с эксепшеном. Согласитесь, все же лучше проверить заранее наличие нужного свободного места и при необходимости до выполнения миграции добавить его.
Вы можете проверить размер вашей таблицы через python manage.py dbshell :
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size FROM pg_database ORDER BY pg_database_size(datname) DESC;
проверить свободное место на диске можно с помощью команды:
df -h
Совет 2: будьте аккуратны с ORM при миграциях
Иногда при миграциях нам требуется стереть или перезаписать данные в существующих полях таблицы. И если вы не сталкивались с такого рода сюрпризами, то скорее всего сделаете это в обычной Django парадигме:
# Generated by Django 5.2.7 on 2026-02-22 08:52 import time from django.db import migrations def change_data(apps, schema_editor): Report = apps.get_model('pages', 'Report') for obj in Report.objects.filter(is_active=True): obj.сumulative_layout_shift = 0.05 obj.save() class Migration(migrations.Migration): dependencies = [ ("pages", "0006_alter_sigmaapi_report"), ] operations = [ migrations.RunPython(change_data), ]
В этом коде две коварных ошибки. Обе они очень быстро сжирают оперативную память, а затем упираются в различные блокировки, что опять приводит ваш сервер к многочасовому даунтайму в случае больших гигабайтных таблиц. С таблицами поменьше будет тоже не очень приятно.
Вот пример более производительный, но не лучший:
# Generated by Django 5.2.7 on 2026-02-22 08:52 import time from django.db import migrations def change_data(apps, schema_editor): Report = apps.get_model('pages', 'Report') for obj in Report.objects.filter(is_active=True).iterator(): obj.сumulative_layout_shift = 0.05 obj.save(update_fields=['сumulative_layout_shift']) class Migration(migrations.Migration): dependencies = [ ("pages", "0006_alter_sigmaapi_report"), ] operations = [ migrations.RunPython(change_data), ]
В этом примере мы используем .iterator() и update_fields= Первый помогает избежать быстрого перерасхода оперативной памяти, чтобы не парализовать наш сервер, а второй ускоряет операции обновления.
Но если у вас десятки и сотни тысяч записей в таблице, то лучше не делайте так! В этом случае я бы совсем отказаться от использования obj.save() А вместо него использовал метод .update()
Почему Report.objects.filter(is_active=True).iterator() все же не лучшее решение? Дело в том, что хоть мы и экономим память при использовании итератора, за кулисами Django для каждого объекта будет выполнять запрос SELECT. Таким образом, если у нас 10000 объектов и сама запись тяжелая, скажем 1 Мб+, то эта миграция запустит 10000 раз SELECT и, как следствие, мы сильно потеряем время на дисковых операциях и транспортировке данных.
Вот наиболее производительный пример для этого кейса. В нем совсем нет SELECT-ов, а только один UPDATE.
# Generated by Django 5.2.7 on 2026-02-22 08:52 import time from django.db import migrations def change_data(apps, schema_editor): Report = apps.get_model('pages', 'Report') Report.objects.filter(is_active=True).update(сumulative_layout_shift=0.05) class Migration(migrations.Migration): dependencies = [ ("pages", "0006_alter_sigmaapi_report"), ] operations = [ migrations.RunPython(change_data), ]
Подведем итоги
Перед миграциями проверяйте доступное свободное пространство на диске. Убедитесь, что на диске свободного места в 2 раза больше, от занимаемого объема.
В коде с миграциями старайтесь избегать обход по объектам и метод
.save(). Вместо этого используйте.update()Если вам по каким-то причинам не избежать этого, используйте.iterator()иupdate_fields=
Иначе вы рискуете попасть в неприятную ситуацию, в которой оказывался я — вместо ожидаемых 10 минут обновлений, просидеть 8 часов за терминалом с даунтаймом своего проекта.
К слову о своих проектах. В этом году я начал развивать свой новый проект — https://sigmaapi.com/ru/ Если у вас есть среди знакомых SEO специалисты, отправьте им ссылку на него. Буду вам очень признателен. Это поможет мне быстрее собрать правильную обратную связь от них и развивать продукт в правильном направлении.
