В Django есть довольно полезная функция генерации Migration, но при добавлении поля с ForeignKey в существующую модель, вам необходимо передать соответствующее начальное значение во время Migration, поскольку данное поле НЕ ограничено NULL.
Конкретно в код были внесены следующие изменения. (Предположим, что вы создали некоторые данные для модели Post, но хотите сгруппировать сообщения по тегам).
Существующий кодекс
class Post(models.Model):
text = models.TextField("本文",blank=False)
После добавления
class Tag(models.Model):
name = models.CharField("名前",max_length=10,blank=True)
class Post(models.Model):
text = models.TextField("本文",blank=False)
tag = models.ForeignKey(Tag,on_delete=CASCADE)
В этом случае существующие данные затрудняют добавление полей с помощью миграции, как обычно.
Поэтому рассмотрим способ создания данных для модели тега и привязки всех существующих постов к сгенерированному тегу за одну генерацию миграции.
Если мы запустим manage.py makemigrations
после внесения изменений в предыдущий код, нам понадобится значение по умолчанию, потому что он добавляет столбец NOT NULL к существующим данным! Что вы хотите делать? Вам будет предложено передать одноразовое значение по умолчанию, поэтому вы можете передать соответствующее значение (первоначально здесь можно было написать код на Python, чтобы создать данные модели Tag и передать значение… но, насколько я пытался, это оказалось невозможным. Но сколько я ни пытался, мне не удалось этого добиться).
Затем генерируется следующий код миграции.
class Migration(migrations.Migration):
dependencies = [
('application', '0011_auto'),
]
operations = [
migrations.AddField(
model_name='post',
name='tag',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to='application.tag'),
preserve_default=False,
),
]
Естественно, если вы запустите его как есть, результат будет не очень хорошим, поэтому не запускайте команду migrate, а измените файл миграции.
Политика процесса заключается в следующем
- Добавление столбцов без ограничений NOT NULL
- Создайте пока данные для модели Tag и обновите их, чтобы связать с существующими данными.
- Добавить ограничения NOT NULL
Порядок процесса следующий.
В миграции есть функция RunPython, которая выполняет функции во время миграции, поэтому мы используем ее. Эта функция очень мощная, потому что она также может определять функцию для Rollback.
Что касается функций, вызываемых в RunPython, аргументы apps и schema_editor используются для подключения к БД и получения моделей, как описано в документации.
Исходя из вышесказанного, ниже приведена модифицированная миграция, которая привязывает все существующие посты к тегу ‘Ежедневник’, согласно вышеупомянутому процессу.
def create_tag(apps, schema_editor):
db_alias = schema_editor.connection.alias
Tag = apps.get_model("application", "Tag")
tag = Tag.objects.using(db_alias).create(name="日常日記")
Post = apps.get_model("application", "Post")
Post.objects.using(db_alias).update(tag=tag)
class Migration(migrations.Migration):
dependencies = [
('application', '0011_auto'),
]
operations = [
migrations.AddField(
model_name='post',
name='tag',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='application.tag'),
preserve_default=False,
),
migrations.RunPython(create_tag),
migrations.AlterField(
model_name='post',
name='tag',
field=models.ForeignKey(null=False, on_delete=django.db.models.deletion.CASCADE, to='application.tag'),
preserve_default=False,
),
]
Теперь мы можем добавить внешний ключ без уничтожения существующих данных за одну миграцию.
Однако мне кажется, что существует более разумный способ сделать это, поэтому если вы знаете такой способ, я буду очень благодарен, если вы сообщите мне об этом в разделе комментариев.