首先在模型上添加一个唯一非空的 uuid 字段:
import uuid
from django.db import models
class MyModel(models.Model):
#...
uuid = models.UUIDField(default=uuid.uuid4, unique=True)
执行迁移:
python manage.py makemigrations myapp
因为是向已有模型添加 唯一非空字段,这会导致数据表中已有的数据该字段的值不能确定,默认迁移并不能在每一个已有数据上分别执行 uuid.uuid4()
函数来设置不同值。
因此生成迁移文件时会出现下面情况:
Callable default on unique field chat.uuid will not generate unique values upon migrating.
Please choose how to proceed:
1) Continue making this migration as the first step in writing a manual migration to generate unique values described here: https://docs.djangoproject.com/en/4.1/howto/writing-migrations/#migrations-that-add-unique-fields.
2) Quit and edit field options in models.py.
这里我们直接输入 1 即可,先强制生成迁移文件。
检查迁移文件列表,我们会发现多了一个包含 AddField 操作的迁移文件,形如 0004_mymodel_uuid.py,内容如下:
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
("app_workflow", "0047_remove_pipelinereport_creator_and_more"),
]
operations = [
migrations.AddField(
model_name="chat",
name="uuid",
field=models.UUIDField(default=uuid.uuid4, unique=True),
),
]
此时直接执行迁移是行不通的,需要我们在迁移中手动给数据表中已经存在的数据设置字段值,以确保唯一性约束和非空约束。
首先我们修改 0004_mymodel_uuid.py 文件,将其中的 "unique=True" 改成 "null=True",这样会先创建一个值可以重复但不能为空的字段。
然后我们新建一个空的迁移用来为已经存在的数据手动设置字段值:
python manage.py makemigrations myapp --empty
这将生成形如 0005_auto_20240517_1100.py 的迁移文件,内容如下:
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("app_workflow", "0004_mymodel_uuid"),
]
operations = []
首先将其重名成 0005_populate_mymodel_uuid_values.py(注意将 mymodel 替换成实际的模型名称)。
然后重写内容,使用 migrations.RunPython
操作来更新字段值:
from django.db import migrations
import uuid
def gen_uuid(apps, schema_editor):
MyModel = apps.get_model("myapp", "MyModel")
for row in MyModel.objects.all():
row.uuid = uuid.uuid4()
row.save(update_fields=["uuid"])
class Migration(migrations.Migration):
dependencies = [
("myapp", "0004_mymodel_uuid"),
]
operations = [
# omit reverse_code=... if you don't want the migration to be reversible.
migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
]
migrations.RunPython
的第一个参数是正向操作,reverse_code
是逆向操作。
migrations.RunPython.noop()
函数表示一个空操作。
在正向操作 gen_uuid()
函数中,我们取出模型已有的数据,然后分别为其设置 uuid 值,在客观上实现唯一非空。
最后我们还需要一个恢复字段唯一性约束和非空约束的操作,我们再次新建一个空迁移文件:
python manage.py makemigrations myapp --empty
将其重命名为 0006_remove_mymodel_uuid_null.py(注意将 mymodel 替换成实际的模型名称)。
修改内容,使用 AlterField 操作来恢复唯一性约束:
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
("myapp", "0005_populate_mymodel_uuid_values"),
]
operations = [
migrations.AlterField(
model_name="chat",
name="uuid",
field=models.UUIDField(default=uuid.uuid4, unique=True),
),
]
最后我们执行迁移:
python manage.py migrate myapp
Operations to perform:
Apply all migrations: myapp
Running migrations:
Applying myapp.0004_mymodel_uuid... OK
Applying myapp.0005_populate_mymodel_uuid_values... OK
Applying myapp.0006_remove_mymodel_uuid_null... OK
参考资料:
评论区