← Назад к вопросам
Как создаются миграции в 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
- Всегда коммитируй миграции — они часть кода
- Не редактируй миграции вручную — пересоздай, если нужны изменения
- Для больших таблиц используй кастомные миграции — можно добавить индексы осторожнее
- Объединяй лишние миграции (squash) — для чистоты истории
- Тестируй откат — убедись, что migrate откатывается корректно
- Синхронизируй с другими разработчиками — избегай конфликтов
- Используй default для NOT NULL полей — иначе миграция сломается
- Для уникальных полей сначала добавь без 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!