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

Django migrations, calculate new fields value based on old fields before deleting old fields?

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

在这种情况下,是否可以 运行 迁移我们创建新字段的地方,根据模型旧的开始结束字段给它一个值

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)

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

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

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

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

因此您可以创建自定义迁移:

python manage.py makemigrations --name migration_name app_name --empty

然后在那里定义你需要的一系列操作:

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 ...),
     ]

编辑:

您的迁移应该如下所示(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 ...),
    ]

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

现在,如果您希望能够回滚迁移,则必须定义第二个函数,其作用与 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()

更多信息在这里: https://docs.djangoproject.com/en/4.0/ref/migration-operations/