【问题标题】:Django migrations, calculate new fields value based on old fields before deleting old fields?Django迁移,在删除旧字段之前根据旧字段计算新字段值?
【发布时间】:2022-01-05 07:00:59
【问题描述】:

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

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

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

【问题讨论】:

  • 一个 DateField 包含一年,但您没有存储年份,您从哪里获得年份?
  • 年份相对不重要。旧系统假定假定具体时期,每年都有特定的开始和结束日期。但是,从那以后我被告知我们可能有 18 个月的周期,这在旧系统中是不可行的。

标签: python django django-models django-migrations


【解决方案1】:

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

  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/

【讨论】:

  • 这似乎非常接近我正在寻找的东西。我需要为 RunPython 指定参数,还是直接传递函数的名称?另外,这会在数据库中为每个项目运行所述函数,还是我需要自己进行查询?
  • 已编辑。虽然我不确定我是否理解了你的第二个问题。
  • 你的例子完美地回答了我的问题。我需要在函数中进行查询并保存模型的每个实例。我在我的本地 SQLite 数据库上运行迁移,它工作。感谢您的帮助!
猜你喜欢
  • 2014-06-15
  • 1970-01-01
  • 1970-01-01
  • 2021-09-07
  • 2016-05-07
  • 2023-02-23
  • 2020-08-08
  • 2023-03-30
  • 1970-01-01
相关资源
最近更新 更多