← Назад к вопросам
Зачем нужен механизм миграций в Django?
1.6 Junior🔥 261 комментариев
#Django#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Django Миграции — управление эволюцией БД
Миграции в Django — это система управления изменениями схемы базы данных. Это критически важный инструмент для команд разработчиков и production систем.
Основная проблема без миграций
Без Django миграций (ужас)
-- Разработчик 1 меняет схему
ALTER TABLE users ADD COLUMN phone_number VARCHAR(20);
-- Разработчик 2 независимо делает:
ALTER TABLE users ADD COLUMN country VARCHAR(50);
-- При merge коммитов — конфликты
-- При deployment в production — ???
-- Кто-то забыл migration script — база рассинхронизирована
Результат: chaos и downtime.
Что решают миграции
1. Версионирование схемы БД
Каждое изменение модели создает версионированную миграцию:
# Миграция автоматически создается
python manage.py makemigrations
# Создается файл: migrations/0001_initial.py
# Это как git commit для БД
2. Воспроизводимость
# migrations/0001_initial.py
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(primary_key=True)),
('email', models.EmailField(unique=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
],
),
]
Теперь любой, в любой момент, может:
python manage.py migrate # Применить все миграции
И база будет точно такой же, как на продакшене.
3. Пошаговое развертывание (Zero Downtime)
# миграция 1: добавляем новое поле с дефолтом
class Migration(migrations.Migration):
operations = [
migrations.AddField(
model_name='user',
name='phone_number',
field=models.CharField(
max_length=20,
null=True, # Может быть пусто
blank=True
),
),
]
Теперь мы можем:
# Обновить код на production
class User(models.Model):
email = models.EmailField()
phone_number = models.CharField(max_length=20, null=True)
# Миграция применится БЕЗ downtime'а
4. Откат (Rollback)
# Что-то сломалось? Откатываемся
python manage.py migrate users 0001
# Или посмотрим историю
python manage.py showmigrations users
# applied (X) migrations
[X] 0001_initial
[X] 0002_add_phone_field
[X] 0003_add_country_field <- сломалась, откатываем
[ ] 0004_fix_schema
Практические примеры
Пример 1: Добавление нового поля
# models.py
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
# Добавляем новое поле
sku = models.CharField(max_length=50, unique=True)
# Django автоматически создаст миграцию
python manage.py makemigrations
# Created migration products/migrations/0002_product_sku.py
# Применяем
python manage.py migrate
# Running migrations:
# Applying products.0002_product_sku ... OK
Пример 2: Пользовательская миграция (Data Migration)
python manage.py makemigrations --empty products --name populate_sku
# migrations/0003_populate_sku.py
from django.db import migrations
import uuid
def populate_sku(apps, schema_editor):
Product = apps.get_model('products', 'Product')
for product in Product.objects.all():
if not product.sku:
# Генерируем SKU из ID и названия
product.sku = f"SKU-{product.id}-{uuid.uuid4().hex[:6]}"
product.save()
def reverse_populate(apps, schema_editor):
Product = apps.get_model('products', 'Product')
Product.objects.all().update(sku=None)
class Migration(migrations.Migration):
dependencies = [
('products', '0002_product_sku'),
]
operations = [
migrations.RunPython(populate_sku, reverse_populate),
]
Пример 3: Переименование поля
# Старое имя: publish_date -> Новое: published_at
python manage.py makemigrations --name rename_publish_date
# migrations/0004_rename_publish_date.py
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('blog', '0003_populate_sku'),
]
operations = [
migrations.RenameField(
model_name='article',
old_name='publish_date',
new_name='published_at',
),
]
Пример 4: Удаление поля
# Сначала: удаляем из models.py поле
class User(models.Model):
email = models.EmailField()
# phone_number удалили
# Django создаст миграцию
python manage.py makemigrations
# Created migration users/migrations/0005_user_remove_phone_number.py
Пример 5: Создание индекса
# migrations/0006_add_indexes.py
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('products', '0005_product_sku'),
]
operations = [
migrations.AddIndex(
model_name='product',
index=models.Index(
fields=['sku'],
name='products_sku_idx'
),
),
migrations.AddIndex(
model_name='product',
index=models.Index(
fields=['name', 'price'],
name='products_name_price_idx'
),
),
]
Workflow с миграциями
1. Разработчик меняет model
↓
2. python manage.py makemigrations (создает файл миграции)
↓
3. python manage.py migrate (применяет локально)
↓
4. Тестирует работу
↓
5. git commit (включает миграцию в коммит)
↓
6. git push (в репозиторий)
↓
7. Deploy в production
↓
8. python manage.py migrate (на сервере)
Best Practices
1. Всегда commit миграции
# ❌ ПЛОХО
echo "migrations/" >> .gitignore
# ✓ ХОРОШО
git add migrations/0001_initial.py
git commit -m "Add users model with migrations"
2. Давай описательные имена
# ❌ Плохо
0002_auto_2024_03_22_1230.py
# ✓ Хорошо
0002_add_email_verification_field.py
0003_add_user_preferences_model.py
3. Pequeные, логичные миграции
# ❌ Одна огромная миграция
class Migration(migrations.Migration):
operations = [
migrations.CreateModel(...), # 50 полей
migrations.CreateModel(...), # 30 полей
migrations.CreateModel(...), # 20 полей
]
# ✓ Несколько логичных
# 0001_initial.py - базовые модели
# 0002_add_user_profile.py - профиль
# 0003_add_email_verification.py - верификация
4. Проверяй состояние
python manage.py showmigrations
python manage.py sqlmigrate users 0001
python manage.py migrate --plan
5. Тестируй откаты
# Применяем
python manage.py migrate
# Откатываем на шаг назад
python manage.py migrate users 0002
# Снова применяем
python manage.py migrate
Проблемы и решения
Конфликт миграций в git
# Два разработчика создали 0005_migration.py
# Решение: переименовать одну
# Переименуем вторую на 0006 и обновим dependencies
git mv migrations/0005_migration.py migrations/0006_migration.py
Потерянные миграции в production
# На production есть миграция, которой нет в коде
python manage.py migrate --fake users 0005
# --fake говорит: считай что уже применена, но не выполняй SQL
Альтернатива: Alembic (для SQLAlchemy)
Для non-Django проектов используют Alembic:
alembic revision --autogenerate -m "Add user table"
alembic upgrade head # Применить
alembic downgrade -1 # Откатить
Но в Django используем встроенные миграции.
Итог
Дjango миграции — это version control для БД:
- Воспроизводимость — одна команда и БД синхронизирована
- История — видна каждая запись в схему
- Командная работа — конфликты видны и разрешимы
- Zero downtime — правильная стратегия миграций
- Откат — если что-то сломалось, можно вернуться
- Автоматизация — Django генерирует большую часть
Это обязательный практика в production разработке.