Django对象创建和Postgres序列

6

我有一个导入脚本,它运行一系列命令将东西从一个Postgres数据库复制到另一个数据库,两个数据库都运行相同的Django代码库。大部分情况下,它使用./manage.py loaddata进行复制,但是一些对象需要额外处理,我使用Django的objects.create()方法在自定义脚本中复制数据。在执行此操作时,我会指定ID,例如:

MyObject.objects.create(id = 2, title = 'foo')

一旦脚本完成,我注意到在执行objects.create()时Postgres SEQUENCE在表格上出现错误。即,导入前它是50,在导入后仍是50,即使该表现在有150个对象。当创建新对象时,这会导致错误,因为它尝试使用已经存在的ID(在所有这些表上,ID只是一个普通的自动递增字段)。然而,通过./manage.py loaddata填充的表格似乎没问题。
我知道可以使用Django的./manage.py sqlsequenreset手动重置这些表格,但我想知道为什么序列似乎首先出现了问题。objects.create()没有增加它吗?我是否忽略了一些显而易见的东西?

有没有更好的同步数据库的方法?你检查了自增id字段的表结构吗? - piyer
2
我对Postgres特定的内容不是很了解,但我的猜测是,每当插入一行具有标识列的“null”时,Postgres会将其“填充”(类似于auto_increment),并且序列会递增。如果您自己指定ID,则会绕过此逻辑并直接插入该行。我猜Django没有尝试实际递增序列号,而是将其留给数据库引擎。 - shylent
@shylent 我也是凭直觉飞行,但似乎在数据库中递增一个序列变量的目的是为了避免在每次插入记录时运行 ids 的 max(当未指定 id 时)。 为了在 OP 的用例中保持序列最新,必须在指定 id 时进行 max 运行。 我认为要求用户手动请求刷新是合理的。 - David Berger
2个回答

12

一切运作良好。Django的create()与序列递增没有直接关系,简而言之:

  • PostgreSQL自动递增('serial'类型)只是“创建序列+创建整数字段并将序列值设置为默认值”的快捷方式
  • Django的autofield主键(如果未经过您指定则为id整数)只是创建了一个序列字段。
  • 当您手动指定id时,Postgres会将该值插入到数据库中。当您指定一个值时,它会省略'default'参数,这是一种合适的行为。

因此,如果您希望您的插入按照您选择的方式递增序列,您需要手动更改序列值,使用SELECT setval('sequence_name', int_value); 否则将其保留为空,它将自动递增 - 选择当前值并将其加1(如果在序列定义中未另行指定)。

另一个想法是首先创建对象,然后更新id值(当然不能已经使用过),最后将序列值设置为max id。


2
自增字段有效,但您必须按以下方式查询:
MyObject.objects.create(title='foo')

没有id字段,这个会在数据库中自动计算。

我猜我在这里感到好奇的是,Django自己的dumpdata命令生成的JSON指定了对象的PK,并且loaddata命令似乎尊重这些PK,而不会使Postgre序列混乱。那么为什么objects.create无法做到呢?也许这只是一个疏忽? - KRH
@KRH 我认为 django/core/management/commands/load.py:188 中的代码可以解答我们这个问题。 - diegueus9
明白了。我有点惊讶于objects.create()没有做到这一点,考虑到它可能会引发主键错误,但我认为从设计的角度来看,他们认为这是用户需要解决的问题。 - KRH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接