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 еще не существует.