编写Django迁移,该迁移基于另一​​个字段添加唯一字段

时间:2018-06-27 22:45:54

标签: python django

我有一个具有唯一字段SessionCategory的模型namename引用了该模型的某些关键实例;唯一的问题是name在我们的内部仪表板中也是可编辑的,因此用户可以通过更改name来无意间“破坏”这些引用。

为解决此问题,我想创建一个新字段name_slug,它是name的简化版本,也具有唯一的约束。我尝试执行以下操作:

from django.db import models
from django.utils.text import slugify
from django.db.models.signals import pre_save
from django.dispatch import receiver



class SessionCategory(models.Model):
    name = models.CharField(max_length=255, unique=True)
    name_slug = models.CharField(max_length=255, unique=True)


@receiver(pre_save, sender=SessionCategory)
def create_name_slug(sender, instance, **kwargs):
    if not instance.name_slug:
        instance.name_slug = slugify(instance.name)

我在其中添加了name_slug唯一字段。问题是,如果我尝试python manage.py makemigrations,会收到以下提示:

(venv) Kurts-MacBook-Pro-2:lucy-web kurtpeek$ python manage.py makemigrations
You are trying to add a non-nullable field 'name_slug' to sessioncategory without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: 

我在https://docs.djangoproject.com/en/dev/howto/writing-migrations/#migrations-that-add-unique-fields遇到了一个工作流,该工作流用于编写创建唯一字段的迁移。在该示例中,使用“通用”函数uuid.uuid4()生成唯一字段。但是,就我而言,我需要访问特定实例的name,就像我希望通过连接到pre_save信号获得的那样。

如何创建此迁移并维护唯一约束?

1 个答案:

答案 0 :(得分:3)

我认为RunPython功能会为您提供帮助。

第1步。在添加name_slug字段之前,您已经有迁移文件。然后使用name_slug = models.CharField(max_length=255, unique=True,null=True)创建一个新的迁移文件。该文件将如下所示

app_name/migrations/0003_some_name.py

from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('sample', '0006_sessioncategory'),
    ]

    operations = [
        migrations.AddField(
            model_name='sessioncategory',
            name='name_slug',
            field=models.CharField(max_length=255, null=True, unique=True),
        ),
    ]


第二步。通过使用RunPython如下添加自定义迁移脚本,

from __future__ import unicode_literals

from django.db import migrations, models
from django.utils.text import slugify


class Migration(migrations.Migration):
    def forwards_func(apps, schema_editor):
        SessionCategory = apps.get_model("sample", "SessionCategory")
        for instance in SessionCategory.objects.all():
            if not instance.name_slug:
                instance.name_slug = slugify(instance.name)
                instance.save()

    def reverse_func(apps, schema_editor):
        pass

    dependencies = [
        ('sample', '0006_sessioncategory'),
    ]

    operations = [
        migrations.AddField(
            model_name='sessioncategory',
            name='name_slug',
            field=models.CharField(max_length=255, null=True, unique=True),
        ),
        migrations.RunPython(forwards_func, reverse_func

                             )
    ]



第3步。通过python manage.py migrate进行迁移
而已 !



警告!
不要在第1步上运行 python manage.py migrate命令(仅makemigrations命令)

注意
迁移文件是在我的本地系统中生成的,用于重现行为。对于您来说,可能有所不同