← Назад к вопросам

Как создаются миграции в Django?

1.3 Junior🔥 181 комментариев
#Django#Базы данных (SQL)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Создание миграций в Django

Миграции — это версионирование схемы БД. Django автоматически отслеживает изменения моделей и генерирует SQL. Это мощный инструмент, но требует понимания и осторожности.

Автоматическое создание миграций

# Создать файл миграции на основе изменений моделей
python manage.py makemigrations

# Создать миграцию для конкретного приложения
python manage.py makemigrations myapp

# Создать пустую миграцию (для custom SQL)
python manage.py makemigrations --empty myapp --name my_custom_migration

# Проверить SQL без применения
python manage.py sqlmigrate myapp 0001

# Применить миграции
python manage.py migrate

# Применить миграции только для приложения
python manage.py migrate myapp

# Откатить к конкретной миграции
python manage.py migrate myapp 0001

# Откатить все миграции приложения
python manage.py migrate myapp zero

Полный жизненный цикл

# Шаг 1: Изменить модель (models.py)
from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    # НОВОЕ ПОЛЕ
    published = models.BooleanField(default=False)
    
    class Meta:
        db_table = "article"

# Шаг 2: Создать миграцию
# python manage.py makemigrations
# Создаст: myapp/migrations/0002_article_published.py

# Шаг 3: Проверить SQL
# python manage.py sqlmigrate myapp 0002
# CREATE TABLE ... ALTER TABLE ...

# Шаг 4: Применить миграцию
# python manage.py migrate

# Шаг 5: Использовать новое поле

Типы изменений и генерируемые миграции

# 1. ДОБАВЛЕНИЕ ПОЛЯ
class User(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(new=True)  # Генерирует AddField

# 2. УДАЛЕНИЕ ПОЛЯ
# Просто удали из models.py, миграция удалит из БД

# 3. ПЕРЕИМЕНОВАНИЕ ПОЛЯ
class User(models.Model):
    full_name = models.CharField(max_length=100)  # было name
# Django спросит: "Did you rename...?"

# 4. ИЗМЕНЕНИЕ ТИПА ПОЛЯ
class Product(models.Model):
    price = models.FloatField()  # было IntegerField

# 5. ДОБАВЛЕНИЕ ИНДЕКСА
class Post(models.Model):
    title = models.CharField(max_length=200, db_index=True)  # новый индекс
    
    class Meta:
        indexes = [models.Index(fields=["title", "created_at"])]

# 6. ДОБАВЛЕНИЕ ОГРАНИЧЕНИЯ
class Article(models.Model):
    slug = models.SlugField(unique=True)  # unique констрейнт

Кастомные миграции (Data Migrations)

python manage.py makemigrations --empty myapp --name populate_user_slugs

Генерируемый файл (myapp/migrations/0003_populate_user_slugs.py):

from django.db import migrations
from django.utils.text import slugify

def populate_slugs(apps, schema_editor):
    """Функция для применения миграции"""
    User = apps.get_model("myapp", "User")
    
    for user in User.objects.all():
        user.slug = slugify(user.name)
        user.save()

def reverse_slugs(apps, schema_editor):
    """Функция для отката миграции"""
    User = apps.get_model("myapp", "User")
    User.objects.all().update(slug="")

class Migration(migrations.Migration):
    dependencies = [
        ("myapp", "0002_user_slug"),  # зависимость от предыдущей миграции
    ]
    
    operations = [
        migrations.RunPython(populate_slugs, reverse_slugs),
    ]

Сложные операции с миграциями

from django.db import migrations, models

class Migration(migrations.Migration):
    dependencies = [
        ("myapp", "0001_initial"),
    ]
    
    operations = [
        # Добавить поле
        migrations.AddField(
            model_name="user",
            name="email",
            field=models.EmailField(default="", unique=True),
            preserve_default=False,
        ),
        
        # Переименовать поле
        migrations.RenameField(
            model_name="user",
            old_name="username",
            new_name="login",
        ),
        
        # Удалить поле
        migrations.RemoveField(
            model_name="user",
            name="old_field",
        ),
        
        # Создать индекс
        migrations.AddIndex(
            model_name="user",
            index=models.Index(
                fields=["email"],
                name="user_email_idx"
            ),
        ),
        
        # Выполнить SQL
        migrations.RunSQL(
            sql="UPDATE myapp_user SET active = true WHERE created_at > 2020-01-01;",
            reverse_sql="UPDATE myapp_user SET active = false WHERE created_at > 2020-01-01;",
        ),
    ]

Обработка конфликтов миграций

# Конфликт: две ветки создали 0003_migration
# Решение: "squash" миграции

python manage.py squashmigrations myapp 0001 0003
# Создаст: 0001_squashed_0003_migration.py
# Объединит все 3 миграции в одну для новых инстанций

Миграции с ForeignKey и M2M

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=200)
    # ДОБАВЛЯЕМ FK
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

# Миграция потребует значение по умолчанию для существующих записей
# python manage.py makemigrations
# "Provide a one-off default now..."
# Варианты:
# 1. Указать значение (author_id = 1)
# 2. Создать кастомную миграцию с populate функцией

Миграции на продакшене

# Перед деплоем на продакшен:

# 1. Проверить, что миграции применяются успешно
python manage.py migrate --plan

# 2. Протестировать откат (если нужен rollback)
python manage.py migrate myapp 0002
python manage.py migrate myapp 0003

# 3. Создать backup БД
dbctl backup production

# 4. Применить миграции
python manage.py migrate

# 5. Проверить, что всё работает
python manage.py check
python manage.py test

Best Practices

  1. Всегда коммитируй миграции — они часть кода
  2. Не редактируй миграции вручную — пересоздай, если нужны изменения
  3. Для больших таблиц используй кастомные миграции — можно добавить индексы осторожнее
  4. Объединяй лишние миграции (squash) — для чистоты истории
  5. Тестируй откат — убедись, что migrate откатывается корректно
  6. Синхронизируй с другими разработчиками — избегай конфликтов
  7. Используй default для NOT NULL полей — иначе миграция сломается
  8. Для уникальных полей сначала добавь без unique=True — потом обнови данные, потом добавь constraint

Сложная миграция (best practice пример)

from django.db import migrations, models

class Migration(migrations.Migration):
    dependencies = [
        ("myapp", "0001_initial"),
    ]
    
    operations = [
        # Шаг 1: Добавить новое поле БЕЗ ограничений
        migrations.AddField(
            model_name="user",
            name="slug",
            field=models.SlugField(default=""),
        ),
        
        # Шаг 2: Заполнить данные
        migrations.RunPython(
            code=populate_slugs,
            reverse_code=migrations.RunPython.noop,  # Нечего откатывать
        ),
        
        # Шаг 3: Добавить ограничение
        migrations.AlterField(
            model_name="user",
            name="slug",
            field=models.SlugField(unique=True),
        ),
    ]

def populate_slugs(apps, schema_editor):
    User = apps.get_model("myapp", "User")
    for user in User.objects.all():
        user.slug = slugify(user.name)
        user.save()

Миграции — это не страшно, когда ты понимаешь основные концепции и следуешь best practices!