Django迁移,在删除旧字段之前根据旧字段计算新字段值?

Man*_*mon 5 python django django-models django-migrations

我们打算将我们的模型之一从旧的开始结束日期值重新设计为使用开始日期和长度。然而,这确实带来了挑战,因为我们想要为新字段提供默认值。

在这种情况下,是否可以在我们创建新字段的地方运行迁移,并根据模型旧的起始字段为其赋予一个值

from datetime import date as Date
from django.db import models
    
class Period(models.Model):
    
    #These are our new fields to replace old fields
    period_length=models.IntegerField(default=12)
    starting_date=models.DateField(default=Date.today)
    
    #Old fields. We want to calculate the period length based on these before we remove them
    start_day = models.IntegerField(default=1)
    start_month = models.IntegerField(default=1)
    end_day = models.IntegerField(default=31)
    end_month = models.IntegerField(default=12)
Run Code Online (Sandbox Code Playgroud)

开始月份和结束月份可以是 1 到 12 之间的任意数字,因此我们需要进行大量计算才能获得正确的长度。有没有一种方法可以让我们在迁移中运行一个函数,在添加新字段之后,在调用删除旧字段之前计算它们的新值?

我确实知道我可以使用 makemigrations 创建基本的添加/删除字段,但我想在两者之间添加值计算。我考虑过的其他选择是首先运行迁移来添加字段,然后运行自定义命令来计算字段,然后进行第二次迁移来删除旧字段,但这感觉更有可能破坏某些内容。

Mar*_*cel 10

我要做的是创建一个自定义迁移并在那里定义以下一系列操作:

  1. 添加长度字段。
  2. 通过计算更新长度字段。
  3. 删除旧字段。

因此,您可以使用以下命令创建自定义迁移:

python manage.py makemigrations --name migration_name app_name --empty
Run Code Online (Sandbox Code Playgroud)

然后定义您需要的一系列操作:

operations = [
         migrations.AddField (... your length field...),
         migrations.RunPython (... the name of your function to compute and store length field ...),
         migrations.RemoveField (... your end_date field ...),
     ]
Run Code Online (Sandbox Code Playgroud)

编辑:

您的迁移应该如下所示(update_length_field将是您的函数,具有相同的参数):

class Migration(migrations.Migration):

    dependencies = [
        ('app_name', 'your_previous_migration'),
    ]

    def update_length_field(apps, schema_editor):
        for period in Period.objects.all():
            period.length = ... whatever calculations you need ...
            period.save()

    operations = [
        migrations.AddField (... your length field...),
        migrations.RunPython(update_length_field),
        migrations.RemoveField (... your end_date field ...),
    ]
Run Code Online (Sandbox Code Playgroud)

在基本层面上,它会是这样的。

现在,如果您希望迁移能够回滚,则必须定义第二个函数,该函数的作用与update_length_field的作用完全相反。并将其作为migrations.RunPython的第二个参数。

此外,如果您希望迁移与模型的未来更改兼容(如果迁移仅部署一次,则没有必要),您必须从代码的历史版本中获取模型,例如:

    def update_length_field(apps, schema_editor):
        Period = apps.get_model("app_name", "Period")
        for period in Period.objects.all():
            period.length = ...
            period.save()
Run Code Online (Sandbox Code Playgroud)

更多信息请参见: https://docs.djangoproject.com/en/4.0/ref/migration-operations/