Django 中外键的默认值 migrations.AddField

Default value for foreign key in Django migrations.AddField

使用migrations,我需要向模型添加一个新字段(外键)。我知道可以用:

    migrations.AddField(
        model_name='MyModel',
        name='state',
        field=models.ForeignKey(null=True, related_name='mymodel_state', to='msqa_common.MyModelState'),
    ),

但是,我不希望我的字段可以为空。相反,我想为它使用一个默认值,对应于名称为 "available" 的 MyModelState 的 id(id 值可能会在不同的机器上发生变化)。 table MyModelState 的 "available" 值是在之前的迁移脚本中插入到数据库中的,因此它确实存在。

我想我应该这样做:

    migrations.AddField(
        model_name='MyModel',
        name='state',
        field=models.ForeignKey(null=False, default=available_state_id, related_name='mymodel_state', to='msqa_common.MyModelState'),
    ),

我的问题:如何在迁移脚本中获取 available_state_id

你不能直接做。这样做的推荐方法是创建一个迁移以使用 null=True 添加它,然后添加一个使用 Python 或 SQL 的数据迁移来更新所有现有的指向 available_state_id,然后第三次迁移将其更改为 null=False。

我刚遇到同样的问题,偶然发现了这个答案,所以我是这样做的:

  operations = [
        # We are forced to create the field as non-nullable before
        # assigning each Car to a Brand
        migrations.AddField(
            model_name="car",
            name="brand",
            field=models.ForeignKey(
                null=True,
                on_delete=django.db.models.deletion.PROTECT,
                to="model.Brand",
            ),
        ),

        # assign_car_to_brand loops over all my Car objects and sets their
        # "brand" field
        migrations.RunPython(add_category_to_tags, do_nothing),

        # Make the field non-nullable to force all future Car to have a Brand
        migrations.AlterField(
            model_name="car",
            name="brand",
            field=models.ForeignKey(
                null=False,
                on_delete=django.db.models.deletion.PROTECT,
                to="model.Brand",
            ),
            preserve_default=False
        ),

    ]

这里有一个比较完整的例子:

第一步

python manage.py makemigrations,设置临时默认值None

第二步

将生成的迁移代码更改为以下样式

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


def set_default_author_to_blog(apps, schema_editor):
    User = apps.get_model("auth", "User")
    Blog = apps.get_model("blog", "Blog")
    Blog.objects.update(author=User.objects.first())


def revert_set_default_autor_to_blog(apps, schema_editor):
    Blog = apps.get_model("blog", "Blog")
    Blog.objects.update(author=None)


class Migration(migrations.Migration):
    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('blog', '0001_auto_20220425_1017'),
    ]

    operations = [
        migrations.AddField(
            model_name='blog',
            name='author',
            field=models.ForeignKey(null=True, db_constraint=False, on_delete=django.db.models.deletion.PROTECT,
                                    to='auth.user', verbose_name='Author')
        ),
        migrations.RunPython(set_default_author_to_blog, reverse_code=revert_set_default_autor_to_blog),
        migrations.AlterField(
            model_name='blog',
            name='author',
            field=models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.PROTECT,
                                    to='auth.user', verbose_name='Author')
        ),
    ]

第三步

python manage.py migrate