【问题标题】:How to set default value for new model field in existing model with data如何使用数据为现有模型中的新模型字段设置默认值
【发布时间】:2019-02-24 18:20:25
【问题描述】:

我有一个带有两个字段的小型 django 模型。该模型的数据库中已有数据。

class MetaDataValue(Model):
    metadata_id = models.ForeignKey(MetaData, on_delete=models.CASCADE,)
    value = models.CharField('value', max_length=200,)

我需要添加另一个字段,

short_value = models.CharField('short_value', max_length=200,)

我知道当我执行迁移时,它会抱怨,因为我没有数据库中现有行的默认值。

有没有办法将short_value 的默认值设置为数据库/模型中已经存在的value 字段中的字符串?

我想这样做是因为我只需要为数据库中大约 20 行创建一个不同的short_value,并且该字段没有通用默认值。我宁愿在short_value 字段中没有“fred”或“default”之类的东西,因为有些字段有数字,有些有文本,有些有数字和文本的组合。我也想过创建一个属性而不是另一个模型字段,但是没有一种简单的方法可以将value 字段转换为short_value 字段。

谢谢!

【问题讨论】:

  • 是的,只需添加字段,进行迁移,Django 会要求填写默认值。

标签: django django-models


【解决方案1】:

您可以简单地将字段添加到模型并调用:

python3 manage.py makemigrations

Django 将提示您为此填写“一次性”默认值。例如,如果我在您的模型上运行它,我会看到:

You are trying to add a non-nullable field 'short_value' to metadatavalue without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
 2) Quit, and let me add a default in models.py
Select an option: 1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> 'default'
Migrations for 'app':
  app/migrations/0002_metadatavalue_short_value.py
    - Add field short_value to metadatavalue

(黑体部分是我自己写的)。

因此,此处的行将文本 default 作为值。

“一次性”值不是为您的模型指定的默认值。它只是一个存储在迁移文件中的值,并添加到可能已经存在的行中。

我们可以对迁移文件进行后期编辑,并为该列提供一个默认值,然后运行一个基本上将值从另一列复制到新列的函数,例如:

# Generated by Django 2.0.2 on 2019-02-24 19:45

from django.db import migrations, models

def copy_value_to_short_value(apps, schema_editor):
    MetaDataValue = apps.get_model('app', 'metadatavalue')
    db_alias = schema_editor.connection.alias
    from django.db.models import F
    MetaDataValue.objects.using(db_alias).all().update(
        short_value=F('value')
    )


class Migration(migrations.Migration):

    dependencies = [
        ('app', '0001_initial_commit'),
    ]

    operations = [
        migrations.AddField(
            model_name='metadatavalue',
            name='short_value',
            field=models.CharField(default='value', max_length=200, verbose_name='short_value'),
            preserve_default=False,
        ),
        migrations.RunPython(copy_value_to_short_value),
    ]

因此,我们定义了一个类似于 Django ORM 查询的函数 copy_value_to_short_value,然后我们将 migrations.RunPython(copy_value_to_short_value) 添加到迁移中应该完成的任务中。

您当然应该在运行迁移之前编辑迁移文件,否则迁移会出现在django_migrations 表中,并且 Django 将迁移视为“完成” ”。

【讨论】:

  • 我试图避免您建议的解决方案。我不希望将值“默认”存储在现有行中。我希望将现有字段“value”中的当前字符串设置为现有行的新字段“short_value”中的值。
  • @user1045680:您可以在迁移文件中添加一个额外的函数来进行迁移,然后将值从一列复制到另一列。
  • 感谢您的更新。它看起来正是我需要的,虽然比我想要的更复杂! ;) 我曾希望 django 有一个更简单的解决方案。在使用 'default' 值运行迁移之后,对于 mysql (UPDATE table SET short_value=value) 来说似乎是一个简单的衬线,可以实现相同的目标。
【解决方案2】:

类似于威廉的回答: 在提供一次性默认值后,使用类似“default”的字符串 ,只需将 SQL 添加到您的迁移操作中:

migrations.RunSQL("UPDATE metadatavalue SET short_value = value;")

【讨论】:

  • 为什么会有帮助?这能回答问题吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多