Django 重命名字段并创建同名的新字段会返回 psycopg2 错误:DuplicateTable:关系已存在

tak*_*kje 0 python django postgresql django-migrations

我有两个 Django(外键)字段 - FieldA 和 FieldB - 引用同一个类,但是不同的对象。然后我想将 FieldB 重命名为 FieldC。我在一次迁移中重命名了它(自动检测)。

然后我意识到我实际上需要一个与旧 FieldB 同名的新字段(也是同一类的外键)。因此,我创建了第二个迁移来添加新字段:FieldB。由于我刚刚重命名了另一个,我认为这不会给数据库带来任何问题。

我在本地使用 SQLite DB 进行开发,效果很好。当我将其推送到我们的 Postgres 数据库时,这返回了一个错误。

模型类

class ModelClass(Model):
    field_a: ForeignClassA = models.ForeignKey(ForeignClassA, on_delete=models.SET_NULL, blank=True, null=True, related_name='FieldA')
    # this one should be renamed to field_c after which I create another field with the same name and properties.
    field_b: ForeignClassA = models.ForeignKey(ForeignClassA, on_delete=models.SET_NULL, blank=True, null=True, related_name='FieldB')
Run Code Online (Sandbox Code Playgroud)

迁移一:重命名

operations = [
        migrations.RenameField(
            model_name='modelname',
            old_name='FieldB',
            new_name='FieldC',
        ),]
Run Code Online (Sandbox Code Playgroud)

迁移二:添加字段

operations = [
        migrations.AddField(
            model_name='modelname',
            name='FieldB',
            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='FieldB', to='app.othermodel'),
        ),]
Run Code Online (Sandbox Code Playgroud)

当我运行此迁移时,出现以下错误

  Applying app.xxx1_previous... OK
  Applying app.xxx2_rename_field... OK
  Applying app.xxx3_add_field...Traceback (most recent call last):
  File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.DuplicateTable: relation "app_modelname_fieldB_id_8c448c6a" already exists
Run Code Online (Sandbox Code Playgroud)

通常该列应被删除并替换为具有新名称的新列。我发现这个问题描述了 Postgress 中的类似问题,现在我想知道这是否是 Django 中的错误。难道是重命名后的清理工作没有正确完成?

编辑1

仔细检查 Postgres DB 后,我可以看到,重命名后,我的表中仍然有两列:FieldA_id 和 FieldB_id,而我希望有 FieldA_id 和 FieldC_id。显然,如果我随后尝试再次添加 FieldB,这会产生问题。

难道 Postgres(或 Django 控制器)由于某种原因没有重命名该列?

编辑2

我检查了对 Postgres 数据库的 SQL 查询。生成以下 SQL:

  Applying app.xxx1_previous... OK
  Applying app.xxx2_rename_field... OK
  Applying app.xxx3_add_field...Traceback (most recent call last):
  File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.DuplicateTable: relation "app_modelname_fieldB_id_8c448c6a" already exists
Run Code Online (Sandbox Code Playgroud)

然而,此重命名似乎仅部分执行,因为下一步报告了问题,但迁移成功了。

但是,当我手动运行此 SQL 查询时,该命令成功并且升级部分成功。总之:

  1. SQL 查询有效
  2. 运行 SQL 时,列名会更新
  3. 运行 SQL 时,约束会更新
  4. 下一步仍然抱怨对此重命名的引用。目前还不清楚这可能是什么。

tak*_*kje 6

经过长时间的 SQL 兔子洞搜索后,我发现 PostgresQL 的重命名迁移不会删除旧索引。

当我想创建一个新字段时,它尝试创建一个与旧索引(未删除)同名的新索引。

避免这种情况的一个简单方法是将重命名与两个更改操作“夹在中间”。一种是取消设置索引,另一种是在之后将其设置回来。

operations = [
        migrations.AlterField(
            model_name='modelclass',
            name='field_b',
            field=models.ForeignKey(blank=True, db_index=False, null=True, on_delete=django.db.models.deletion.SET_NULL,
                                    to='otherapp.otherclass'),
        ),
        migrations.RenameField(
            model_name='modelclass',
            old_name='field_b',
            new_name='field_c',
        ),
        migrations.AlterField(
            model_name='modelclass',
            name='field_c',
            field=models.ForeignKey(blank=True, db_index=True, null=True, on_delete=django.db.models.deletion.SET_NULL,
                                    to='otherapp.otherclass'),
        ),
    ]
Run Code Online (Sandbox Code Playgroud)

我也在 Django bug tracker 上报告了这个问题,之后他们关闭了它,因为7 年前就存在重复的问题。