Удалить наследование модели и сохранить идентификатор во вновь созданном автозаполе

У меня есть модель, которая наследуется от неабстрактной супер модели:

class Person(models.Model):
    pass

class User(Person):
    pass

Из-за ошибки зачатия мы хотим удалить модель Person и скопировать все данные о пользователях пользователям. Основная проблема касается "id" AutoField.

Пользовательская модель не имеет поля идентификатора, автоматически созданного django. Из-за наследования первичным ключом пользователя является "person_ptr_id".

Я пытаюсь сделать миграцию базы данных. Миграции Django хотят добавить поле "id" в качестве AutoField, но запрашивают значение по умолчанию. Я хочу, чтобы начальное значение было скопировано из person_ptr_id для каждой записи пользователя. Я также хочу, чтобы последовательность postgres синхронизировалась со значениями.

Вы уже выполнили такие миграции?

2 ответа

Решение

Я наконец преуспел, делая это с этими миграциями:

1) Измените автоматическую миграцию django, комментируя удаление столбца person_ptr и создание столбца id. Затем добавьте столбец id как целое число:

migrations.AddField(
    model_name='user',
    name='id',
    field=models.IntegerField(null=True)
),

2) Создайте новую пустую миграцию для переноса данных от человека к пользователю, удалите поле person_ptr и измените тип 'id' на AutoField

def copy_persons_data(apps, schema_editor):
    User = apps.get_model("accounts", "User")
    Person = apps.get_model("persons", "Person")
    for user in User.objects.all():
        person = Person.objects.get(id=user.person_ptr_id)
        user.new_field1 = person.new_field1
        user.new_field2 = person.new_field2
        user.id = person.id
        user.save()

class Migration(migrations.Migration):

    dependencies = [
        ('accounts', '0026_auto_20170606_1524'),
    ]

    operations = [
        migrations.RunPython(copy_persons_data, reverse_code=migrations.RunPython.noop),
        migrations.RemoveField(
            model_name='user',
            name='person_ptr',
        ),
        migrations.AlterField(
            model_name='user',
            name='id',
            field=models.AutoField(auto_created=True, null=False, primary_key=True, serialize=False, verbose_name='ID'),
            preserve_default=False,
        ),
    ]

При этом django автоматически создаст последовательность для поля 'id', синхронизированного с max id.

Заметки:

Использование user.person_ptr в функции миграции данных заставит django отказаться от сохранения пользователя из-за несохраненного экземпляра лица. Поэтому я делаю запрос get, чтобы получить экземпляр лица пользователя.

perserve_default = false важно, чтобы избежать необходимости django в новых миграциях для удаления значения по умолчанию для автополя

По моему опыту, лучше всего работало:

  • Переименуйте старую модель и создайте для нее миграцию.
  • Воссоздайте новую модель с правильной иерархией и снова создайте для нее миграцию.
  • Создайте миграцию данных и заполните новую модель старыми значениями.
  • Удалите старую модель и выполните миграцию снова.

Это был, безусловно, менее извилистый путь. Надеюсь, это поможет кому-то с той же проблемой.

Другие вопросы по тегам