Ответ
Кастомные миграции в Django необходимы, когда стандартные возможности makemigrations и migrate недостаточны для выполнения сложных операций с базой данных или данными. Это позволяет выполнять произвольный Python-код или SQL-запросы в процессе миграции.
Основные сценарии:
- Сложные преобразования данных: При изменении структуры модели, которое требует миграции существующих данных. Например, разделение одного поля на несколько, объединение полей, изменение типа данных с преобразованием значений.
- Выполнение произвольного SQL: Для оптимизации миграций на больших таблицах, использования специфичных для СУБД функций, создания индексов с особыми параметрами или выполнения других операций, не поддерживаемых Django ORM напрямую.
- Интеграция с внешними системами: Если миграция затрагивает данные или схемы в других базах данных или внешних сервисах, которые не управляются Django.
- Создание или изменение не-модельных объектов БД: Например, создание хранимых процедур, триггеров, представлений (views) или функций базы данных.
- Откат сложных изменений: Для определения обратной логики (
reverse_func) при откате миграции, особенно еслиforwards_funcвыполнял необратимые изменения данных.
Пример кастомной миграции (Python):
# app_name/migrations/000X_custom_data_migration.py
from django.db import migrations, models
def forwards_func(apps, schema_editor):
# Получаем модель User из текущего состояния проекта
User = apps.get_model('auth', 'User')
# Получаем доступ к базе данных
db_alias = schema_editor.connection.alias
# Пример: Обновление поля 'first_name' для всех пользователей
# Допустим, мы хотим добавить префикс "User_" к каждому имени
for user in User.objects.using(db_alias).all():
user.first_name = f"User_{user.first_name}"
user.save()
def backwards_func(apps, schema_editor):
# Логика отката: удаляем префикс
User = apps.get_model('auth', 'User')
db_alias = schema_editor.connection.alias
for user in User.objects.using(db_alias).all():
if user.first_name and user.first_name.startswith("User_"):
user.first_name = user.first_name[len("User_"):]
user.save()
class Migration(migrations.Migration):
dependencies = [
('auth', '0012_alter_user_first_name_max_length'), # Зависимость от предыдущей миграции
]
operations = [
migrations.RunPython(forwards_func, backwards_func),
# Можно также использовать migrations.RunSQL для выполнения сырых SQL-запросов
# migrations.RunSQL("ALTER TABLE myapp_mymodel ADD COLUMN new_field INT DEFAULT 0;",
# "ALTER TABLE myapp_mymodel DROP COLUMN new_field;")
]
Использование migrations.RunPython или migrations.RunSQL позволяет гибко управлять эволюцией схемы и данных базы данных, выходя за рамки автоматической генерации миграций Django.