Как выполнить произвольный Python-код в Django-миграции?

Ответ

Для выполнения произвольного Python-кода в Django-миграциях используется операция migrations.RunPython. Это необходимо для сложных операций с данными, которые нельзя выразить через стандартные изменения схемы, например, для миграции данных между полями, создания начальных объектов или выполнения кастомной логики.

RunPython принимает два аргумента:

  1. forward_func: Функция, которая будет выполнена при применении миграции.
  2. reverse_func: Функция, которая будет выполнена при отмене миграции (опционально, но рекомендуется).

Обе функции получают аргументы apps (исторический реестр приложений) и schema_editor.

Пример:

from django.db import migrations

def create_initial_data(apps, schema_editor):
    # Получаем модель из исторической версии приложения 'myapp'
    MyModel = apps.get_model('myapp', 'MyModel')
    # Создаем объекты
    MyModel.objects.create(name='Test Item 1', value=10)
    MyModel.objects.create(name='Test Item 2', value=20)

def reverse_create_initial_data(apps, schema_editor):
    # Удаляем созданные объекты при откате миграции
    MyModel = apps.get_model('myapp', 'MyModel')
    MyModel.objects.filter(name__startswith='Test Item').delete()

class Migration(migrations.Migration):
    dependencies = [
        # Укажите зависимости от предыдущих миграций
        ('myapp', '0001_initial'),
    ]
    operations = [
        migrations.RunPython(
            create_initial_data,
            reverse_create_initial_data
        ),
    ]

Важно: Всегда используйте apps.get_model('app_label', 'ModelName') вместо прямого импорта моделей (from myapp.models import MyModel). Это гарантирует, что вы работаете с исторической версией модели, которая существовала на момент создания данной миграции, а не с текущей версией, что предотвращает ошибки при изменении схемы в будущем.