[英]Why do I need to run the postgresql nextval function? And how to prevent it?
我只是遇到了我不了解的Django和PostgreSQL问题。
我有一个简单的模型,定义如下:
class MyModel(models.Model):
my_field = models.IntegerField()
my_other_field = models.TextField()
在我看来,我有类似的东西:
my_object = MyModel(my_field=1, my_other_field='blah')
my_object.save()
直到今天早上,一切都很好。 我收到此错误:
IntegrityError at /my_url/
duplicate key value violates unique constraint "my_model_pkey"
DETAIL: Key (id)=(3) already exists.
CONTEXT: Remote SQL command: INSERT INTO public.my_model(id, my_field, my_other_field) VALUES ($1, $2, $3) RETURNING id
我曾经遇到这个错误,我知道它与PostgreSQL用id列同步与我的模型关联的顺序表的方式有关。 我有PostgreSQL中,直到运行此功能id
返回比最大的值id
。
select nextval('my_model_id_seq'::regclass);
我的问题是:为什么首先发生这种情况? 以及将来如何预防呢?
顺便说一句,这是我将数据插入表中的唯一方法,我从未手动插入数据。
我希望这个问题足够清楚
我认为问题不是“为什么我的序列弄乱了”-而是“为什么Django在插入行时为什么要尝试为id列提供一个值,而不是允许数据库在序列中插入下一个值”。
Django文档描述了用于确定在调用save()时应执行UPDATE还是INSERT的算法。
该算法涉及检查对象的“ id”字段是否已设置为某个值。 如果不是,则执行INSERT(可能未为“ id”字段指定值)。 如果已设置,则它首先尝试执行UPDATE; 如果这不会导致更新记录,则将执行INSERT(这一次可能会为“ id”字段指定一个值)。
正如Erwin的答案所指出的那样,您看到的错误消息表明它正在尝试在为'id'字段指定值时插入一行。
我注意到,似乎该算法在Django 1.6版中已更改。 以前,它先使用SELECT来查看是否存在记录,然后使用UPDATE或不插入INSERT。 如果自升级以来开始出现问题,则可能是原因。 文档说明:
在极少数情况下,即使数据库包含对象主键值的行,数据库也不会报告行已更新。 一个示例是PostgreSQL ON UPDATE触发器,该触发器返回NULL。 在这种情况下,可以通过将select_on_save选项设置为True来恢复到旧算法。
如果发生这种情况,那么它将解释您的症状:尝试更新数据库中的值时实际上会发生错误,而django会错误地认为该行不存在,然后尝试创建该行。
您可以通过将'select_on_save'设置为true来恢复到原来的行为来进行检查。
另一个可能的原因是,如果您的代码无意间将对象的'id'属性设置为某个值,然后调用save()。 根据该值是否已经存在于数据库中,这可能会导致各种问题。 特别是,这可能会导致创建一个行,该行的“ id”值位于与该列关联的序列的当前范围之前,因此以后您在尝试插入该行时会遇到错误。
另一个可能的原因可能是在先前从数据库加载的行上,对保存()使用了'force_insert'参数(因此,它实际上是您应该更新的现有行)。
问题的根源在这里(错误消息中的SQL命令):
INSERT INTO public.my_model(id, my_field, my_other_field)
VALUES ($1, $2, $3)
RETURNING id
由于你的id列似乎是一个serial
类型, 不要手动插入值。 让默认值自动从序列中绘制。 应该:
INSERT INTO public.my_model(my_field, my_other_field)
VALUES ($1, $2)
RETURNING id;
这就是添加RETURNING id
开头的全部要点:返回新生成的id
。 如果您自己输入值,则无需返回它。
如果序列由于某种原因不同步,因为手动输入与nextval()
的数字冲突,请运行一次此查询:
SELECT setval('my_model_id_seq', max(id)) FROM my_model;
这会将序列设置为当前最大值。 下一个呼叫是下一个号码,没有一次关闭错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.