В каких случаях нужны кастомные миграции в Django и как их создать

Ответ

Кастомные миграции в Django необходимы, когда стандартных операций makemigrations (изменение схемы БД) недостаточно для обновления состояния базы данных.

Когда это необходимо:

  • Миграция данных (Data Migration): Заполнение новых полей на основе значений существующих, преобразование формата данных в колонке.
  • Выполнение сырых SQL-запросов: Создание триггеров, хранимых процедур или использование специфичных для СУБД функций, которые не поддерживаются Django ORM.
  • Зависимости от внешних систем: Например, если нужно синхронизировать данные с внешним API после изменения схемы.
  • Сложные переименования или реструктуризация: Когда простое переименование поля в модели может привести к потере данных.

Как создать и использовать:

  1. Создать пустой файл миграции:
    python manage.py makemigrations --empty yourappname
  2. Написать код миграции: В сгенерированном файле используются операции 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() для экономии памяти.