当前位置: 首页 > 知识库问答 >
问题:

django.db.utils.IntegrityError:重复的键值违反了唯一约束“spirit_category_category_pkey”

甄伟兆
2023-03-14

我正在使用django和精神来构建一个网站。在一次测试中,当我将新数据插入一个名为spirit_category_category的表时,我得到了以下错误:

django.db.utils.IntegrityError: duplicate key value violates unique constraint "spirit_category_category_pkey"
DETAIL:  Key (id)=(1) already exists.

请注意,表中已经有另外两条 id 为 12 的记录。因此,插入键(id)=(1)当然行不通。但是执行的 sql 不包含 id 字段。也就是说,密钥 (id)=(1) 是由 postgresql 自动生成的,为什么它生成一个已经存在的 ID?

为了找出原因,我在postgresql中运行了以下命令:

test_spiritdb=# select start_value, last_value, max_value from spirit_category_category_id_seq;
 start_value | last_value |      max_value      
-------------+------------+---------------------
           1 |          1 | 9223372036854775807
(1 row)

因此,基本上,last_value1,因此postgresql每次都会生成键(id)=(1)

test_spiritdb=# alter sequence spirit_category_category_id_seq restart with 3;

测试通过了。但这是一个测试,所以更改测试表是没有意义的,因为测试数据库将被删除并为每个测试再次创建,所以下次测试将再次失败,因为last_value仍然会生成为1。所以我想知道为什么django/postgresql会为last_value生成这样一个异常值?如何修复它?类别模型和迁移如下,如果有帮助的话。

# -*- coding: utf-8 -*-

from __future__ import unicode_literals

from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.core.urlresolvers import reverse
from django.conf import settings

from .managers import CategoryQuerySet
from ..core.utils.models import AutoSlugField

class Category(models.Model):

    parent = models.ForeignKey('self', verbose_name=_("category parent"), null=True, blank=True)

    title = models.CharField(_("title"), max_length=75)
    slug = AutoSlugField(populate_from="title", db_index=False, blank=True)
    description = models.CharField(_("description"), max_length=255, blank=True)
    is_global = models.BooleanField(_("global"), default=True,
                                    help_text=_('Designates whether the topics will be'
                                                'displayed in the all-categories list.'))
    is_closed = models.BooleanField(_("closed"), default=False)
    is_removed = models.BooleanField(_("removed"), default=False)
    is_private = models.BooleanField(_("private"), default=False)

    # topic_count = models.PositiveIntegerField(_("topic count"), default=0)

    objects = CategoryQuerySet.as_manager()

    class Meta:
        ordering = ['title', 'pk']
        verbose_name = _("category")
        verbose_name_plural = _("categories")

    def get_absolute_url(self):
        if self.pk == settings.ST_TOPIC_PRIVATE_CATEGORY_PK:
            return reverse('spirit:topic:private:index')
        else:
            return reverse('spirit:category:detail', kwargs={'pk': str(self.id), 'slug': self.slug})

    @property
    def is_subcategory(self):
        if self.parent_id:
            return True
        else:
            return False
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import spirit.core.utils.models


class Migration(migrations.Migration):

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Category',
            fields=[
                ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=True, auto_created=True)),
                ('title', models.CharField(verbose_name='title', max_length=75)),
                ('slug', spirit.core.utils.models.AutoSlugField(db_index=False, populate_from='title', blank=True)),
                ('description', models.CharField(verbose_name='description', max_length=255, blank=True)),
                ('is_closed', models.BooleanField(verbose_name='closed', default=False)),
                ('is_removed', models.BooleanField(verbose_name='removed', default=False)),
                ('is_private', models.BooleanField(verbose_name='private', default=False)),
                ('parent', models.ForeignKey(null=True, verbose_name='category parent', to='spirit_category.Category', blank=True)),
            ],
            options={
                'ordering': ['title', 'pk'],
                'verbose_name': 'category',
                'verbose_name_plural': 'categories',
            },
        ),
    ]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
from django.conf import settings


def default_categories(apps, schema_editor):
    Category = apps.get_model("spirit_category", "Category")

    if not Category.objects.filter(pk=settings.ST_TOPIC_PRIVATE_CATEGORY_PK).exists():
        Category.objects.create(
            pk=settings.ST_TOPIC_PRIVATE_CATEGORY_PK,
            title="Private",
            slug="private",
            is_private=True
        )

    if not Category.objects.filter(pk=settings.ST_UNCATEGORIZED_CATEGORY_PK).exists():
        Category.objects.create(
            pk=settings.ST_UNCATEGORIZED_CATEGORY_PK,
            title="Uncategorized",
            slug="uncategorized"
        )


class Migration(migrations.Migration):

    dependencies = [
        ('spirit_category', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(default_categories),
    ]
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

    dependencies = [
        ('spirit_category', '0002_auto_20150728_0442'),
    ]

    operations = [
        migrations.AddField(
            model_name='category',
            name='is_global',
            field=models.BooleanField(default=True, help_text='Designates whether the topics will bedisplayed in the all-categories list.', verbose_name='global'),
        ),
    ]

共有3个答案

蓝逸仙
2023-03-14

我也有同样的问题,但我需要保留ID,因为我正在从另一台服务器恢复数据并保留关系等。我的解决方案是在迁移文件上添加另一个命令,以便在插入项目后运行,并重置相关表的数据库序列。

要获得重置表序列的命令,可以运行<code>python manage。py:sqlsequencereset:spirit如上所述https://docs.djangoproject.com/en/1.9/ref/django-admin/#sqlsequencereset

然后在迁移0002_auto_20150728_0442.py文件时添加:

from django.db connection

def reset_spirit_pk_sequence(apps, schema_editor):
    with connection.cursor() as cursor:
        cursor.execute("RESULT_FROM_SQLRESETSEQUENCE")
    row = cursor.fetchone()

...
...

operations = [
    migrations.RunPython(default_categories),
    migrations.RunPython(reset_spirit_pk_sequence),
]

请注意,将RESULT_FROM_SQLRESETSEQUENCE替换为从与您遇到问题的表相关的manage.pysqlreset序列中获得的命令行(用\转义内部")。

糜雪峰
2023-03-14

我不认为问题出在您的迁移上。

您正在尝试添加多个相同的<code>spirit_category_category。一种选择是识别冲突测试,并将它们移动到单独的<code>TestCase

另一种选择是使用像Factory Boy这样的库来创建模型实例,这将有助于避免这样的冲突。

杭永安
2023-03-14

经过大量调试,我终于找到了解决方案。原因是我试图插入两个具有指定id的另一个类别,这将导致postgresql停止增加相对序列last_value。如下所示:

if not Category.objects.filter(pk=settings.ST_TOPIC_PRIVATE_CATEGORY_PK).exists():
    Category.objects.create(
        pk=settings.ST_TOPIC_PRIVATE_CATEGORY_PK,
        title="Private",
        slug="private",
        is_private=True
    )

if not Category.objects.filter(pk=settings.ST_UNCATEGORIZED_CATEGORY_PK).exists():
    Category.objects.create(
        pk=settings.ST_UNCATEGORIZED_CATEGORY_PK,
        title="Uncategorized",
        slug="uncategorized"
    )

解决这个问题的方法很简单,要么在django中手动更改last_value,要么不指定id,即删除以下行:

....
pk=settings.ST_TOPIC_PRIVATE_CATEGORY_PK,
....
pk=settings.ST_UNCATEGORIZED_CATEGORY_PK,
....

我想如果让django承担管理<code>id可能不是一个好主意。

 类似资料:
  • 卡住了,我有一个数据库,当我试图让时,它给出了这个错误,如下所示: 以下是整个错误:

  • 我在django应用程序中创建了一个模型,并从pgadmin将数据填充到表中,但现在当我试图从应用程序创建记录时,它抛出了这个完整性错误: 重复的键值违反了唯一约束“packsapp_foo_pkey” 详细信息:键(id)=(4)已经存在。 这是models.py 我是否总是必须从应用程序本身插入数据? Views.py

  • 我必须交换同一表的不同行的属性。 有一列“reference_id”在 DB 中具有唯一的约束。 代码: A 级- B级- 异常跟踪:

  • 我有一个笑话模型: 现在,当我试图迁移最后一行时,我得到了错误。基本上,我想将一个用户链接到Joke对象,因为我已经有了一个数据库,所以我希望默认值为1,这是管理员用户的id(我检查过了...).Makemigrations工作正常,但是当我尝试迁移时,我得到了这个: 我真的不明白怎么了。有什么想法吗?

  • 我在创建应用程序时遇到了这个问题。因此,每当我添加第一条评论时,问题都不会出现,但当我第二次尝试时,我会收到此错误: 重复的键值违反了唯一约束“tripplanner_discussion_author_id_key”详细信息:键 (author_id)=(1) 已存在。 我试图把放到 models.py,但它根本没有帮助。 models.py views.py 更新 当我登录到另一个用户时,一个

  • 当并发客户机试图将数据插入子表时,我们面临唯一的约束冲突问题。 假设我们有1以下的表格。用户user_id、first_name、last_name。2.项目project_idproject_name和project_description。 两者都有着多对多的关系。 当两个客户端试图创建一个新用户时。假设client1创建了user1(id=aa1),子记录项目(id=1)。Client2还创