Удалить наследование модели и сохранить идентификатор во вновь созданном автозаполе
У меня есть модель, которая наследуется от неабстрактной супер модели:
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 в новых миграциях для удаления значения по умолчанию для автополя
По моему опыту, лучше всего работало:
- Переименуйте старую модель и создайте для нее миграцию.
- Воссоздайте новую модель с правильной иерархией и снова создайте для нее миграцию.
- Создайте миграцию данных и заполните новую модель старыми значениями.
- Удалите старую модель и выполните миграцию снова.
Это был, безусловно, менее извилистый путь. Надеюсь, это поможет кому-то с той же проблемой.