South migrate: как поле ForeignKey сделать ManyToMany без потери данных

South migrate: как поле ForeignKey сделать ManyToMany без потери данных

В одном из проектов мне необходимо было ForeignKey превратить в ManyToMany без потери данных. Русского мануала я не нашел для этого. Но нашел статейку на английском и, исправив ошибки в ней, решил сделать русский вариант.

Итак, задача - нужно превратить поля таблицы базы данных с отношенияем FK в M2M отношения при помощи South и при этом не потерять существующие заполненные данные.

1. Вот исходная модель (образец), приложение  назовем appz, допустим.

class TestData(models.Model):
    field1 = models.CharField(max_length=200)
    field2 = models.CharField(max_length=200)

class Knight(models.Model):
    name = models.CharField(max_length=100)
    additional_field_new_name = models.CharField(max_length=155, default='')
    data = models.ForeignKey(TestData)

2. Я добавил новый столбец data_new  для модели Knight (Рыцарь)  с М2М отношением к TestData:

data_new = models.ManyToManyField(TestData, related_name='testdata_info')

3. Запускаем миграцию схемы:

python manage.py schemamigration appz --auto

4. Изменяем метод forward в нашей новой миграции, код который нужно разместить после создания новых полей/прокси модели:

for obj in orm.Knight.objects.all():
    obj.data_new.add(obj.data)
    obj.save()

5. Делаем миграцию:

python manage.py migrate appz

6. Удаляем поле data из модели и делаем миграцию;

# remove column "data" from model
python manage.py schemamigration appz --auto
python manage.py migrate appz

7. переименовываем поле (m2m) data_new в data и запускаем миграцию схемы

# rename m2m column from "data_new" to "data"
python manage.py schemamigration appz --auto

В этот момент нам нужно оставить все существующие данные в прокси-таблице, но South этого не сделает. Поэтому нужно полностью заменить код метода forward в последней сгенерированной миграции на код ниже:

        db.delete_unique('appz_knight_data_new', ['knight_id', 'testdata_id'])
        
        db.rename_table('appz_knight_data_new', 'app1_knight_data')
        db.create_unique('appz_knight_data', ['knight_id', 'testdata_id'])

На заметку: для Postgres (и я думаю для MySQL с таблицами InnoDB тоже) порядок приведенных выше команд важен: вы должны удалить уникальные констрейны перед переименованием проксирующей таблицы.

И после этого делаем миграцию:

python manage.py migrate appz

Таком образом мы выполнили переименование без потери данных.

Успехов :)

Следующая запись

Предыдущая запись

Похожие записи

Комментарии

Еще нет комментариев.

Пингбэки

Оповещения открыты.

Трэкбэки

URL архива

Only authorized users can post comments