Django 将现有字段更改为外键

问题描述 投票:0回答:3

我有一个 Django 模型,以前看起来像这样:

class Car(models.Model):
    manufacturer_id = models.IntegerField()

还有另一种模型,称为

Manufacturer
id
字段指的是。但是,我意识到使用 Django 的内置外键功能会很有用,因此我将模型更改为:

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer)

此更改似乎立即工作正常,查询工作没有错误,但是当我尝试运行迁移时,Django 输出以下内容:

- Remove field manufacturer_id from car
- Add field manufacturer to car

执行此迁移会清除数据库中的所有现有关系,因此我不想这样做。我真的不想进行任何迁移,因为像

Car.objects.get(manufacturer__name="Toyota")
这样的查询工作正常。我想要一个适当的数据库外键约束,但这不是一个高优先级。

所以我的问题是:有没有办法进行迁移或其他方法,让我可以将现有字段转换为外键?我无法使用

--fake
,因为我需要在开发、产品和同事的计算机上可靠地工作。

python python-3.x django django-migrations
3个回答
8
投票

可以进行数据迁移

  1. 添加新字段
  2. 进行数据迁移https://docs.djangoproject.com/en/3.1/topics/migrations/#data-migrations
  3. 删除旧字段

我不确定,可能还有另一种解决方案,您可以将字段重命名为您想要的名称,然后将字段更改为新类型(进行迁移)

operations = [
        migrations.RenameField(
            model_name='car',
            old_name='manufacturer_id',
            new_name='manufacturer',
        ),
        migrations.AlterField(
            model_name='car',
            name='manufacturer',
            field=ForeignKey(blank=True, null=True,  
                  on_delete=django.db.models.deletion.CASCADE
            ),
    ]

0
投票

由于该字段不会收到任何真正的更改(也就是说,组合时发出的所有 SQL 实际上都是 noop),因此您可以简单地告诉 Django 根本不要运行任何 SQL,而只更改 python 中的模型状态。这就是您的模型迁移的样子


class Migration(migrations.Migration):

    dependencies = [
        ('device', '0017_auto_20240723_0936'),
    ]

    operations = [
        migrations.SeparateDatabaseAndState(
            state_operations=[
                migrations.RemoveField(
                    model_name='car',
                    name='manufacturer_id',
                ),
                migrations.AddField(
                    model_name='car',
                    name='manufacturer',
                    field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='device.SinglePhaseDevice'),
                ),
            ],
            database_operations=[]
        )
    ]

但请注意此操作的含义。您可以有效地更改模型,而无需运行任何实际的 DDL。在某些(损坏的)数据库配置中,该列表必须填充一些内容。需要额外注意、测试和审查,以确保

database_operations
确实是空的。

一个简单的验证方法

  1. 准备一个空数据库。
  2. 执行迁移直至最新迁移。
  3. 运行 django 生成的迁移。将新表 DDL 保存在某处。然后回滚这个 django 生成的迁移。
  4. 运行手写迁移。将当前 DDL 与您刚刚保存的 DDL 进行比较。

这两个 DDL 在功能上应该是等效的,除非字段顺序(因为 django 生成的 DDL 可能包含 REMOVE 字段语句,从而弄乱了字段顺序)。如果不是,请尝试向数据库操作添加一些

migrations.AlterField()
操作,直到它们彼此匹配。


-1
投票

我今天也遇到了同样的问题。 无需重命名现有字段或删除现有列即可完成。

  1. 使用 CreateModel 操作更新初始迁移
...
        migrations.CreateModel(
            name="Car",
            fields=[
                (
                    "manufacturer",
                    models.ForeignKey(
                        blank=True,
                        null=True,
                        on_delete=django.db.models.deletion.DO_NOTHING,
                        db_constraint=False,
                        db_index=False,
                        to="Manufacturer",
                    ),
                ),
  1. 检查
    db_constraint
    db_index
    是否为
    True
    模型的
    Car.manufacturer
    字段上的
    Car
    。如果未设置字段,则 True 是默认值。
  2. 运行
    ./manage.py makemigrations
    生成
    AlterField
    迁移,并在
    Car.manufacturer_id
    上具有所需的约束和索引。

第一步不会影响数据库一致性,因为生成的 DDL 对于

IntegerField
ForeignKey
db_constaint=False
db_index=False
是相同的 第二次迁移添加了缺失的约束和索引。

您可以使用

./manage.py sqlmigrate app migration
命令来检查

© www.soinside.com 2019 - 2024. All rights reserved.