2015-01-01 109 views
13

我一直在研究Django Web应用程序的离线版本,并且经常删除某个ModelX的模型实例。删除所有实例后,Django模型实例主键不会重置为1

我已经从管理页面做了这个,并没有遇到任何问题。该模型只有两个字段:名称和顺序,与其他模型没有其他关系。

为新实例提供了下一个可用的pk,这是合理的,当我删除所有实例时,添加一个新实例会得到一个pk = 1,这是我期望的。

将代码在线移动到我的实际数据库我注意到情况并非如此。我需要更改模型实例,所以我删除它们,但令我惊讶的是主键不断增加而不重置为1.

使用Django API进入数据库我已检查并且旧实例消失,但即使添加新实例也会产生一个主键,该主键提取最后一次删除实例的位置,而不是1.

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

回答

14

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

但您应该认识到,这是理想的行为。一个ID唯一标识数据库中的一个实体。因此,它应该只涉及一行。如果该行随后被删除,那么没有理由为什么您需要一个新行来重新使用该ID:如果您这样做了,那么您会在现在被删除的实体之间创建一个混淆,该实体具有该ID,新创建一个重用它。这样做没有意义,你不应该这样做。

+0

我不明白为什么离线版本的行为方式与在线版本不一样。 – pj2452

+0

使用需要在数据库中产生某些测试数据的单元测试会是一种情况,除非您想要在每次测试中生成一个新表。 – Mehdi

+0

您是否在开发中使用与生产中相同的数据库?特别是sqlite释放被删除对象的键以供重用(不幸的是,如果你依赖于键的唯一性)。 https://sqlite.org/autoinc.html – DylanYoung

3

实际上你是从数据库中删除它们还是使用Django删除它们? Django的将不只是从它删除行更改AUTO_INCREMENT为表格,所以如果你想重置主键,你可能要进入你的数据库和:

ALTER TABLE <my-table> AUTO_INCREMENT = 1; 

(这里假设你正在使用MySQL或类似的)。

+0

谢谢。我通过Django管理页面删除了这些实例。这不能通过Django API完成吗? – pj2452

+0

据我所知,并不是如此 - 但请参阅@ knbk的答案,即使用'manage.py'删除和重新创建表。除非你真的需要主键来获取特定的值,否则最好不要担心它们不是从1开始。 – xnx

+0

如上所述,pk生成策略与Django无关,除非该DB没有本地自动增量支持。数据库后端将确定密钥的回收方式。 – DylanYoung

17

我不会说这是一个问题。这是许多数据库系统的默认行为。基本上,表的自动增量计数器是持久的,并且删除条目不会影响计数器。主键的实际价值不会影响性能或任何事情,它只具有美学价值(如果你达到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。如果该表为空,则在重新启动时将完全重置。对于您的离线环境而言,这可能非常常见,而对于您的在线环境则无此功能。

+0

谢谢。我只是好奇为什么我看到我的在线/离线版本之间的不同功能。 – pj2452

+0

@ pj2452你离线使用哪种数据库引擎? – knbk

+0

@ pj2452 Nvm,这可能是因为离线环境中的数据库服务器经常重新启动。我更新了我的答案以反映这一点。 – knbk

3

没有问题,这就是数据库的工作方式。 Django与生成ids没有任何关系,它只是告诉数据库插入一行,并从数据库中获取ID作为响应。每个表的id从1开始,每次插入一行时都会增加。删除行不会导致该标识返回。您通常不应该担心这一点,您只需要知道每行都有唯一的ID。
您当然可以使用数据库命令更改生成表的id的计数器,这取决于您使用的特定数据库系统。

+0

如上所述,我知道这是默认行为,但我不明白为什么离线/在线有所不同。 – pj2452

+0

您的本地数据库将完全相同,并且不会重新分配ID。您可能已经重新创建了整个数据库。 – nima

+0

sqlite采用现有的最高ID + 1,所以这个答案不完全正确。删除最高ID行将导致下一个创建的行使用相同的ID。 – DylanYoung

0

我不知道这个时候加入,但下面的管理命令将所有表中删除所有数据,如果你正在使用SQLite可以自动递增计数器将重置为1

./manage.py sqlflush | psql DATABASE_NAME 
+1

几乎:它会删除所有表中的所有数据*除了迁移表(保留原因显而易见)(该命令专门用于保留迁移表,如果不需要保存,那么你不需要刷新,你只需要一个dropdb + createdb,然后是'manage migrate') –

+0

好的,谢谢澄清! –

1

使用以下shell命令重置主键:

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