【问题标题】:Does Django provide any built-in way to update PostgreSQL autoincrement counters?Django 是否提供任何内置方法来更新 PostgreSQL 自动增量计数器?
【发布时间】:2019-04-05 18:54:53
【问题描述】:

我正在将一个 Django 站点从 MySQL 迁移到 PostgreSQL。数据量不是很大,所以我采取了一个非常简单的方法:我只是使用内置的Django serialize and deserialize 例程创建JSON记录,然后将它们加载到新实例中,循环对象,并将每个对象保存到新数据库中。

这非常好用,有一个小问题:加载所有记录后,当我在加载旧记录后尝试添加新数据时遇到IntegrityError。 MySQL 自动增量 ID 字段的 Postgres 等效项是串行字段,但是当显式指定 id 值时,串行字段的内部计数器不会递增。结果,Postgres 尝试从 1 开始对记录进行编号——已经使用——导致违反约束。 (这是 Django 中的一个已知问题,标记为 wontfix。)

有很多与此相关的问题和答案,但似乎没有一个答案直接在 Django 的上下文中解决了这个问题。 This answer 给出了您需要运行以更新计数器的查询示例,但我尽量避免在可能的情况下进行显式查询。我可以在保存之前简单地删除 ID 字段并让 Postgres 自己进行编号,但是在这种情况下,ForeignKey 引用将被破坏。其他一切都很好!

如果 Django 提供了一个可以智能地处理任何边缘情况的例程,那就太好了。 (这不会修复错误,但它会允许开发人员以一致且正确的方式解决它。)我们真的必须只使用原始查询来修复它吗?看起来很野蛮。

如果真的没有这样的例程,我将简单地执行以下操作,它直接运行上面链接的答案中建议的查询。但在那种情况下,我很想知道这种方法的任何潜在问题,或者关于我可能做错的任何其他信息。例如,我是否应该只修改记录以使用 UUID,如 this suggests

这是原始方法(经过编辑以反映我实际完成的简化版本)。这与Pere Picornell 的答案非常接近,但他对我来说看起来更健壮。

table = model._meta.db_table
cur = connection.cursor()
cur.execute(
    "SELECT setval('{}_id_seq', (SELECT max(id) FROM {}))".format(table, table)
)

【问题讨论】:

  • 您打算进行多少次这种转换,并且您打算再次转换回 MySQL?如果它接近 1 而不是,那么为什么要尝试自动化呢?只需直接在您的查询工具中运行查询,在导入期间关闭 fkey 检查以及书中的任何其他肮脏技巧 - 然后重新打开所有内容并使其漂亮。 (ps:原始 sql 并不野蛮,它很漂亮 :-)
  • @thebjorn,啊,公平点。理论上,在这种情况下它接近 1,但我预计必须为几个不同的项目这样做,所以我希望能够编写一个标准化的导出/导入脚本来处理这个怪癖。 (抱歉——“野蛮”可能不公平。)

标签: python django postgresql


【解决方案1】:

关于辩论:我的案例是一次性迁移,我决定在完成每个表的迁移后立即运行此函数,尽管您可以在怀疑完整性可能被破坏的任何时候调用它。

    def synchronize_last_sequence(model):
        #  Postgresql aut-increments (called sequences) don't update the 'last_id' value if you manually specify an ID.
        #  This sets the last incremented number to the last id
        sequence_name = model._meta.db_table+"_"+model._meta.pk.name+"_seq"
        with connections['default'].cursor() as cursor:
            cursor.execute(
                "SELECT setval('" + sequence_name + "', (SELECT max(" + model._meta.pk.name + ") FROM " +
                model._meta.db_table + "))"
            )
        print("Last auto-incremental number for sequence "+sequence_name+" synchronized.")

我使用您在问题中提出的 SQL 查询所做的。 找到您的帖子非常有用。谢谢!

它应该适用于自定义 PK,但不适用于多字段 PK。

【讨论】:

    【解决方案2】:

    一种选择是在序列化和反序列化期间使用natural keys。这样,当您将其插入 PostgreSQL 时,它会自动增加主键字段并保持所有内容内联。

    这种方法的缺点是您需要为每个模型设置一组不包含 id 的唯一字段。

    【讨论】:

    • 我非常喜欢这种方法。更多的工作,但非常 - 好吧 - 自然!稍等一下,看看其他人要说什么。
    猜你喜欢
    • 2017-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    • 2016-12-24
    • 1970-01-01
    • 2019-10-31
    • 2018-08-18
    相关资源
    最近更新 更多