Django模型实例的主键在所有实例被删除后不会重置为1。

34

我一直在为我的Django Web应用程序制作离线版本,并经常删除某个ModelX的模型实例。

我是从管理页面进行删除的,没有遇到任何问题。该模型只有两个字段:名称和顺序,没有与其他模型的其他关系。

新实例将获得下一个可用的PK,这很合理。当我删除了所有实例时,添加一个新实例会产生pk = 1,这是我所期望的。

将代码上线到我的实际数据库后,我注意到情况并非如此。我需要更改模型实例,因此我将它们全部删除了,但令我惊讶的是,主键继续递增而没有重置回1。

使用Django API进入数据库进行检查,旧实例已经不存在了,但即使添加新实例,它们的主键也会从上一个已删除实例的位置继续递增,而不是从1开始。

想知道是否有人知道这里可能存在的问题。

7个回答

32

我不认为这是一个问题。这是许多数据库系统的默认行为。基本上,表的自增计数器是持久化的,删除条目不会影响计数器。主键的实际值不影响性能或任何内容,它只有美学价值(如果您达到了20亿限制,您很可能有其他问题需要担心)。

如果你真的想重置计数器,你可以删除并重新创建这个表:

python manage.py sqlclear <app_name> > python manage.py dbshell

或者,如果您需要保留应用程序中其他表中的数据,则可以手动重置计数器:

python manage.py dbshell
mysql> ALTER TABLE <table_name> AUTO_INCREMENT = 1;

你在离线和在线应用程序中看到不同行为的最有可能的原因是,自动增量值仅存储在内存中而不是磁盘上。每次数据库服务器重新启动时,它都会被重新计算为MAX(<column>) + 1。如果表为空,则在重新启动时将完全重置。这在你的离线环境中可能经常发生,但在你的在线环境中几乎不会发生。


谢谢。我只是好奇为什么我在线和离线版本之间看到了不同的功能。 - pj2452
@pj2452 你离线使用的数据库引擎是什么? - knbk
@pj2452 别管那个了,很可能是你的离线环境中的数据库服务器经常会被重启。我更新了我的答案以反映这一点。 - knbk
谢谢。现在我想起来了,这样就清楚多了。 - pj2452

27

正如其他人所说,这完全是数据库的责任。

但你应该意识到,这是一种理想的行为。ID 在数据库中唯一标识一个实体。因此,它只应该引用一行。如果那行随后被删除了,你没有理由希望新行重新使用该 ID:如果你这样做,会在现在已删除的实体和重新使用该 ID 的新创建实体之间造成混淆。这样做毫无意义,也不应该这样做。


我不明白的是为什么离线版本的行为方式与在线版本不一样。 - pj2452
使用需要在数据库中生成特定测试数据的单元测试将是一种情况,除非您想为每个测试生成一个新表。 - Mehdi
你在开发和生产中使用的是同一个数据库吗?特别是SQLite会释放已删除对象的键以供重用(不幸的是,如果您依赖于键的唯一性)。https://sqlite.org/autoinc.html - DylanYoung

6

你是从数据库中删除了它们还是使用 Django 删除了它们?仅通过从表中删除行,Django 不会更改 AUTO_INCREMENT,因此,如果要重置主键,您可能需要进入数据库:

ALTER TABLE <my-table> AUTO_INCREMENT = 1;

(假设您正在使用MySQL或类似的数据库。)

谢谢。我通过Django管理员页面删除了这些实例。这不能通过Django API完成吗? - pj2452
据我所知,没有这样的功能 - 但是请参考@knbk的答案,使用manage.py删除和重新创建表。除非您确实需要主键具有特定值,否则最好不要担心它们不从1开始。 - xnx
如上所述,pk生成策略与Django无关,除非该数据库没有本地自动递增支持。数据库后端将确定如何循环使用这些键。 - DylanYoung

3
如果您正在使用SQLite,您可以使用以下Shell命令重置主键:

DELETE FROM your_table; DELETE FROM SQLite_sequence WHERE name='your_table';


3
没有问题,这是数据库的工作方式。Django与生成id无关,它只是告诉数据库插入一行并从数据库响应中获取id。每个表的id从1开始,并且每次插入一行时都会递增。删除行不会导致id后退。通常情况下,您不必担心这个问题,您只需要知道每行都有一个唯一的id。
当然,您可以使用数据库命令更改生成表id的计数器,具体取决于您使用的特定数据库系统。

如上所述,我理解这是默认行为,但我不理解为什么离线/在线会有差异。 - pj2452
您的本地数据库将完全相同,不会重新分配ID。您可能已经重新创建了整个数据库。 - nima
SQLite会取最高现有ID加1,因此这个答案并不完全正确。删除最高ID行将导致相同的ID用于下一个创建的行。 - DylanYoung

2

对于“POSTGRES”数据库的另一种解决方案是通过UI实现。选择您的表格,查找“序列”下拉菜单并选择设置,以此方式调整序列。

例如:

picture from pgadmin


命令行在这里 -> https://dev59.com/zm435IYBdhLWcg3wkA5e - Cornel Ciobanu

0

我不确定这是什么时候添加的,但以下管理命令将删除所有表中的数据,并将自动递增计数器重置为1。

./manage.py sqlflush | psql DATABASE_NAME

1
几乎:它会删除所有表中的所有数据,除了迁移表,出于明显的原因,迁移表将被保留(此命令专门用于保留迁移表。如果您不需要保留,则不需要刷新,只需进行dropdb + createdb,然后执行manage migrate即可)。 - Mike 'Pomax' Kamermans

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