Ответ
Кастомные миграции в Django необходимы, когда стандартных операций makemigrations
(изменение схемы БД) недостаточно для обновления состояния базы данных.
Когда это необходимо:
- Миграция данных (Data Migration): Заполнение новых полей на основе значений существующих, преобразование формата данных в колонке.
- Выполнение сырых SQL-запросов: Создание триггеров, хранимых процедур или использование специфичных для СУБД функций, которые не поддерживаются Django ORM.
- Зависимости от внешних систем: Например, если нужно синхронизировать данные с внешним API после изменения схемы.
- Сложные переименования или реструктуризация: Когда простое переименование поля в модели может привести к потере данных.
Как создать и использовать:
- Создать пустой файл миграции:
python manage.py makemigrations --empty yourappname
- Написать код миграции:
В сгенерированном файле используются операции
migrations.RunPython
для выполнения Python-кода илиmigrations.RunSQL
для SQL-запросов.
Пример: migrations.RunPython
для преобразования данных
Предположим, мы хотим объединить поля first_name
и last_name
в новое поле full_name
.
Файл миграции 0002_populate_full_name.py
:
from django.db import migrations
def combine_names(apps, schema_editor):
# Получаем историческую версию модели, чтобы миграция не зависела от текущего кода
User = apps.get_model('auth', 'User')
for user in User.objects.all():
user.full_name = f"{user.first_name} {user.last_name}".strip()
user.save()
# Функция для отката миграции (опционально, но рекомендуется)
def uncombine_names(apps, schema_editor):
User = apps.get_model('auth', 'User')
User.objects.update(full_name="")
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial'), # Зависит от предыдущей миграции
]
operations = [
migrations.RunPython(combine_names, reverse_code=uncombine_names),
]
Ключевые моменты:
apps.get_model()
: Всегда используйте этот метод вместо прямого импорта модели, чтобы работать с её версией на момент применения миграции.reverse_code
: Предоставляйте функцию для отката изменений, чтобы миграцию можно было отменить.- Производительность: Для больших объемов данных используйте
queryset.iterator()
илиqueryset.bulk_update()
для экономии памяти.