简体   繁体   English

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

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

We are intenting to rework one of our models from old start-end date values to use starting date and length.我们打算从旧的开始结束日期值修改我们的模型之一,以使用开始日期和长度。 However, this does pose a challenge in that we want to give default values to our new fields.但是,这确实带来了挑战,因为我们希望为新字段提供默认值。

In this case, is it possible to run migration where we create new field, give it a value based on models old start-end 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)

Starting months and ending months can be anywhere between 1 and 12, so we need to run bunch of calculations to get the correct length.开始月份和结束月份可以在 1 到 12 之间的任何时间,所以我们需要运行一堆计算来获得正确的长度。 Is there a way for us to run a function in migrations that, after adding the new fields, calculates their new values before calling for removal of the old fields?我们有没有办法在迁移中运行 function,在添加新字段后,在调用删除旧字段之前计算它们的新值?

I do know I can create basic add/remove fields with makemigrations, but I want to add the value calculations in between.我知道我可以使用 makemigrations 创建基本的添加/删除字段,但我想在两者之间添加值计算。 Other option I have considered is to first run a migration to add the fields, then a custom command to calculate fields and then a second migration that deletes the old fields, but this feels like it has greater chance of breaking something.我考虑过的其他选项是首先运行迁移以添加字段,然后运行自定义命令来计算字段,然后进行第二次迁移以删除旧字段,但这感觉它更有可能破坏某些东西。

What I would do is create a custom migration and define the following series of operations there:我要做的是创建一个自定义迁移并在那里定义以下一系列操作:

  1. Add length field.添加长度字段。
  2. Update length field with calculations.使用计算更新长度字段。
  3. Remove old field.删除旧字段。

So you can create a custom migration with:因此,您可以使用以下命令创建自定义迁移:

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

And then define there the series of operations you need:然后在那里定义您需要的一系列操作:

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

Edit:编辑:

Your migration should be something as below ( update_length_field would be your function, with the same parameters):您的迁移应如下所示( update_length_field将是您的 function,具有相同的参数):

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

At a basic level, it would be like this.在基本层面上,它会是这样的。

Now, if you want the migration to be able to be rolled back, you will have to define a second function that does exactly the opposite of what update_length_field does.现在,如果您希望能够回滚迁移,则必须定义第二个 function,它的作用与update_length_field的作用完全相反。 And put it as the second parameter of migrations.RunPython .并将其作为migrations.RunPython的第二个参数。

Also, if you want the migration to be compatible with future changes to the model (this is not necessary if the migration is to be deployed only once), you must take the model from the historical version of the code, something like:此外,如果您希望迁移与 model 的未来更改兼容(如果迁移仅部署一次,则不需要这样做),您必须从代码的历史版本中获取 model,例如:

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM