Ответ
Для выполнения произвольного Python-кода в Django-миграциях используется операция migrations.RunPython. Это необходимо для сложных операций с данными, которые нельзя выразить через стандартные изменения схемы, например, для миграции данных между полями, создания начальных объектов или выполнения кастомной логики.
RunPython принимает два аргумента:
forward_func: Функция, которая будет выполнена при применении миграции.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). Это гарантирует, что вы работаете с исторической версией модели, которая существовала на момент создания данной миграции, а не с текущей версией, что предотвращает ошибки при изменении схемы в будущем.
Ответ 18+ 🔞
Вот же ж, ну и тема подкинули, про миграции в Джанге. Слушай, а ты знаешь, что там бывает, когда стандартными операциями не обойтись? Ну, типа, надо данные перетасовать, начальные записи накидать или какую-то свою, конченую логику выполнить? Так вот, для этого у них есть такая штука — migrations.RunPython. Без неё, блядь, просто никуда.
Работает это дело так: ты пишешь обычную питонячью функцию, а миграция её запускает. Но есть нюанс, ёпта! Главный аргумент — это forward_func, то есть что делать, когда миграцию применяешь. А второй, reverse_func — это на случай, если ты решишь откатиться, как мудак. Его можно и не писать, но тогда при откате будет пиздец и нестыковка, так что лучше написать.
Обе эти функции получают на вход два объекта: apps (это типа архив всех твоих моделей на момент миграции, очень важная хуйня) и schema_editor (ну, это чтобы с базой напрямую работать, если совсем припёрло).
Смотри, вот тебе живой пример, чтобы не быть просто пиздаболом:
from django.db import migrations
def create_initial_data(apps, schema_editor):
# Внимание, хуй с горы! Не импортируй модель напрямую!
# Берём её из исторического архива 'apps'
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 # Назад! (если что)
),
]
И запомни, как Отче наш, блядь: никогда не импортируй модель напрямую через from myapp.models import MyModel. Потому что модель со временем может измениться, а миграция-то останется старая. Получится, что ты пытаешься работать с полями, которых уже нет — и всё, пиздец, вротберунчик. Всегда используй apps.get_model('app_label', 'ModelName'), это даст тебе именно ту версию модели, которая была актуальна на момент создания миграции. Вот так вот, без этого — просто мартышлюшка.