Django - Добавление первичного ключа к существующей модели с данными

Вот существующая модель в приложении djnago:

class Task_Master_Data(models.Model):
    project_id = models.ForeignKey(Project_Master_Data, on_delete=models.CASCADE)
    task_created_at = models.DateTimeField(auto_now_add=True)
    task_updated_at = models.DateTimeField(auto_now=True)
    task_name = models.CharField(max_length=200, null=True)

Я хочу добавить новое поле (которое будет первичным ключом):

    task_id = models.AutoField(primary_key=True, null=False)

Когда я делаю миграции из терминала, он предоставит мне возможность добавить значение по умолчанию, но это, понятно, приведет к ошибке:

UNIQUE constraint failed: new__main_task_master_data.task_id

Каким будет наилучший путь для такого сценария без необходимости удаления всех данных.

Обратите внимание, я использую базу данных sqlite3 по умолчанию.

Это результат старой ошибки, которая до сих пор не исправлена. См. здесь для получения тикета.

Я нашел обходной путь.

Сначала создайте пустой переход (измените taskapp на имя приложения, в котором живет ваш models.py с Task_Master_Data):

python manage.py makemigrations taskapp --empty

Затем скопируйте приведенные ниже миграции в этот файл и запустите python manage.py migrate, но не забудьте скорректировать название приложения (замените taskapp на название вашего приложения) и зависимости (0010_task_master_data следует заменить на название миграции, которая идет перед ним).

Сначала существующие строки в базе данных нуждаются в значениях для нового поля task_id. Я решил эту проблему, создав поле IntegerField с помощью null=True, которое я затем заполняю вручную с помощью команды RunPython. Затем я удаляю поле id (мой предыдущий первичный ключ) и с помощью команды AlterField меняю поле на AutoField. Значение по умолчанию необходимо, но его никогда не следует использовать. Когда вы создаете новый Task_Master_Data, task_id должен быть автоинкрементным.

# Generated by Django 3.0.4 on 2022-05-11 07:13

from django.db import migrations, models


def fill_taskid(apps, schema_editor):
    # be sure to change it to your app here
    Task_Master_Data = apps.get_model("taskapp", "Task_Master_Data")
    db_alias = schema_editor.connection.alias
    tasks = Task_Master_Data.objects.using(db_alias).all()
    for i, task in enumerate(tasks):
        task.task_id = i
    Task_Master_Data.objects.using(db_alias).bulk_update(tasks, ["task_id"])


class Migration(migrations.Migration):
    dependencies = [
        ('taskapp', '0010_task_master_data'),
    ]

    operations = [
        migrations.AddField(
            model_name='task_master_data',
            name='task_id',
            field=models.IntegerField(null=True),
        ),
        migrations.RunPython(fill_taskid),
        migrations.RemoveField(
            model_name='task_master_data',
            name='id',
        ),
        migrations.AlterField(
            model_name='task_master_data',
            name='task_id',
            field=models.AutoField(default=-1, primary_key=True, serialize=False),
            preserve_default=False,
        ),
    ]

Обратите внимание, что в зависимости от состояния вашей базы данных вам может потребоваться откат некоторых миграций. Созданная мной миграция должна работать, если ваша база данных находится в состоянии, когда поле task_id еще не существует.

Вернуться на верх