Django迁移/South:新列使用同一记录的另一个值作为默认值

8
我想给一个已经存在的表添加一列,并为其赋予依赖于现有数据的默认值:
例如,每条记录都有一个“开始日期”。现在我想添加一个“截止日期”列,并将其填充为每个现有记录的“开始日期”值。(即即将到来的记录将能够选择不同的值)
是否有一种友好的方法来实现这个?

不是数据库级别的操作,需要在forms.py文件中完成。 - elmonkeylp
@elmonkeylp 但是这样怎么能针对所有先前的条目呢? - confused00
一旦您在数据库中设置了默认值,就已经完成了。 - elmonkeylp
你可以这样做,在你的模型中编写一个设置值的方法,在保存记录之前在视图中调用它。 - elmonkeylp
我给您一个答案,并为您之前的记录添加了一个解决方案。 - elmonkeylp
3个回答

14

您也可以在South内部完成此操作。唯一的注意事项是需要两个步骤:

  1. A schema migration that adds the open_until column

    from django.db import models
    import datetime
    
    class MyModel(models.Model):
        start_date = models.DateField(),
        open_until = models.DateField(default=datetime.date.today),
    

    $ python manage.py schemamigration --auto appname

  2. A data migration that fills existing rows with the value of that other column

    $ python manage.py datamigration appname populate_open_until

    import datetime
    
    class Migration(DataMigration):
    
        def forwards(self, orm):
            "Set open_until value to that of start_date for existing rows"
            for t in orm.MyModel.objects.all():
                t.open_until = t.start_date
                t.save()
    
        def backwards(self, orm):
            "Revert back to default"
            for t in orm.MyModel.objects.all():
                t.open_until = datetime.date.today
                t.save()
    

(可选) 在第一步中,您可以提供一个临时默认值或将其设置为可选并添加第三步。

  1. 进行模式迁移,使open_until列成为必填项。

1
我收到了“未知命令:'datamigration'。你是不是想说makemigrations?”的错误提示。 - Ren

2

在Python 3.8中,我首先向MyApp模型文件添加字段,代码如下:

from django.db import models
import datetime

class MyModel(models.Model):
    start_date = models.DateField(),
    open_until = models.DateField(default=datetime.date.today),

在运行manage.py makemigrations之后,将这些行添加到新的迁移文件中:
def forward(apps, schema_editor):
    my_model_objects = apps.get_model('MyApp', 'MyModel').objects.all()
    for t in my_model_objects:
        t.open_until = t.start_date
        t.save()

def reverse(apps, schema_editor):
    pass

class Migration(migrations.Migration):
    operations = [
        " the base operations is here " ,
        migrations.RunPython(forward, reverse),
    ]

-3

正如我之前回复你的那样,如果你正在使用框架,那么在数据库级别设置动态默认值是不必要的 :)

我认为最好的方法是在保存记录之前,在视图中设置列的值。

models.py

from django.db import models

class MyModel(models.Model):
    start_date = models.DateField(),
    open_until = models.DateField(),

forms.py 从django.forms导入ModelForm

class MyForm(forms.ModelForm):
    model = MyModel

    fields = ('start_date')

类视图

from django.http import HttpResponse
from django.views.generic import CreateView
from .models import MyModel


MyView(CreateView):
    form_class = MyForm

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST)
        if form.is_valid():
            submitted_form = form.save(commit=False)
            submitted_form.open_until = form.cleaned_data["start_date"]
            submitted_form.save()
            # return an HttpResponse here

对于之前的条目,创建一个视图只调用一次,然后循环遍历所有记录并根据顺序列的值保存新列的值。

类似这样:

from django.http import HttpResponse

def set_open_until_values(request)
    records = MyModel.objects.all()
    for record in records:
        record.open_until = record.start_date
        record.save()
    return HttpResponse("Done!!")

4
这是一堆瞎搞。OP要求迁移旧数据,而不是处理新创建的记录。数据迁移是使用Django的正确方法。创建一个一次性视图?为什么?如果你真的想避免数据迁移,比如说你还在原型阶段,那就从CLI运行那个函数,不需要构建视图...乱搞乱搞乱搞。 - Vajk Hermecz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接