在Django + South中添加一个非null的ForeignKey字段

时间:2013-04-11 19:03:14

标签: django django-south

我使用Django和South作为我的数据库。现在我想在现有模型中添加一个新模型和一个字段,引用新模型。例如:

class NewModel(models.Model):
    # a new model
    # ... 

class ExistingModel(models.Model):
    # ... existing fields

    new_field = models.ForeignKey(NewModel)  # adding this now

现在South明显抱怨我添加了一个非空字段并要求我输入一次性值。但我真正想要的是为每个现有的NewModel实例创建一个新的ExistingModel实例,从而满足数据库要求。这有可能吗?

2 个答案:

答案 0 :(得分:9)

执行此操作的最简单方法是编写使列更改的架构迁移,然后编写数据迁移以正确填写值。根据您使用的数据库,您必须以稍微不同的方式执行此操作。

SQLite的

对于Sqlite,您可以为关系添加一个sentinel值,并使用数据迁移来填充它而不会出现任何问题:

0001_schema_migration_add_foreign_key_to_new_model_from_existing_model.py

from south.db import db
from south.v2 import SchemaMigration
from django.db import models

class Migration(SchemaMigration):

    def forwards(self, orm):
        db.add_column('existing_model_table', 'new_model',
                      self.gf('django.db.models.fields.related.ForeignKey')(default=0, to=orm['appname.new_model']), keep_default=False)

0002_data_migration_for_new_model.py

class Migration(DataMigration):

    def forwards(self, orm):
        for m in orm['appname.existing_model'].objects.all():
            m.new_model = #custom criteria here
            m.save()

这样可以正常使用,没有问题。

Postgres和MySQL

使用MySql,您必须为其提供有效的默认值。如果0实际上不是有效的外键,那么你会收到错误告诉你。

可能默认为1,但有些情况下这不是有效的外键(发生在我身上,因为我们有不同的环境,有些环境发布到其他数据库,所以ID很少匹配(我们使用UUID进行跨数据库识别,正如上帝所预期的那样)。

你得到的第二个问题是南方和MySQL不能很好地融合在一起。部分原因是MySQL没有DDL事务的概念。

为了解决一些您将不可避免地遇到的问题(包括我上面提到的错误以及南方要求您将SchemaMigration中的orm项标记为no-dry-run),您需要更改以上0001脚本执行以下操作:

0001_schema_migration_add_foreign_key_to_new_model_from_existing_model.py

from south.db import db
from south.v2 import SchemaMigration
from django.db import models

class Migration(SchemaMigration):

    def forwards(self, orm):
        id = 0
        if not db.dry_run:
            new_model = orm['appname.new_model'].objects.all()[0]
            if new_model:
                id = new_model.id
        db.add_column('existing_model_table', 'new_model',
                      self.gf('django.db.models.fields.related.ForeignKey')(default=id, to=orm['appname.new_model']), keep_default=False)

然后您可以正常运行0002_data_migration_for_new_model.py文件。

我建议对Postgres和MySql使用上面的相同示例。我不记得第一个例子中Postgres的任何问题,但我确定第二个例子适用于两个(测试过)。

答案 1 :(得分:6)

您希望在此方案中进行数据迁移以补充架构迁移。

South有一个很好的分步教程,介绍如何在文档here中实现这一目标。

在南方,通过两次或三次架构/数据迁移实现所需的结果并不罕见,因为它并不总是可以在一次重击中完成(如果它可以容忍添加非空列,有时取决于底层数据库)没有默认值)。因此,在这种情况下,您可以添加具有默认值的架构迁移,然后使用对象操作进行数据迁移,然后再进行最终的架构迁移。