重复的键值违反了数据库初始化的唯一约束

Duplicate key value violates unique constraint on database initializzation

我有一个博客模型,如果其他字段不为空,我想将 finished 字段设置为 True。我使用脚本填充数据库 (Postgres),但有些东西在初始化时不起作用(数据库是空的,但在迁移之后,所以表存在)。

我的models.py:

class Post(models.Model):
    tags = TaggableManager(blank=True)
    ...

    def save(self, *args, **kwargs):
        super(Post, self).save(*args, **kwargs)
        if self.title_it!='' and self.title_en!='' and self.text_it!='' and self.text_en!='' and self.tags!='':
            self.finished=True
            super(Post, self).save(*args, **kwargs)

我的脚本init.py:

def add_post(author, title_it, title_en, text_it, text_en, created_date, 
    published_date, tags, views):
    p = Post.objects.get_or_create(author=author, title_it=title_it, 
        title_en=title_en, text_it=text_it, text_en=text_en, 
        created_date=created_date, published_date=published_date, 
        views=views)[0]
    for t in tags:
        p.tags.add(t)
    p.save()
    return p

和我运行脚本时的错误:

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

这里是完整的回溯:

Traceback (most recent call last):
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\query.p
y", line 487, in get_or_create
    return self.get(**lookup), False
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\query.p
y", line 403, in get
    self.model._meta.object_name
blog.models.DoesNotExist: Post matching query does not exist.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\backends\utils
.py", line 85, in _execute
    return self.cursor.execute(sql, params)
psycopg2.IntegrityError: ERRORE:  un valore chiave duplicato viola il vincolo un
ivoco "blog_post_pkey"
DETAIL:  La chiave (id)=(1) esiste già.


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "init_poss.py", line 8374, in <module>
    populate()
  File "init_poss.py", line 7311, in populate
    ['Servizio'], 1) #tzinfo=<UTC>
  File "init_poss.py", line 8323, in add_post
    views=views)[0]
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\manager
.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\query.p
y", line 489, in get_or_create
    return self._create_object_from_params(lookup, params)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\query.p
y", line 528, in _create_object_from_params
    raise e
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\query.p
y", line 521, in _create_object_from_params
    obj = self.create(**params)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\query.p
y", line 417, in create
    obj.save(force_insert=True, using=self.db)
  File "D:\progetti\possedimenti\sitopossedimenti\blog\models.py", line 58, in s
ave
    super(Post, self).save(*args, **kwargs)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\base.py
", line 729, in save
    force_update=force_update, update_fields=update_fields)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\base.py
", line 759, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, upda
te_fields)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\base.py
", line 842, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\base.py
", line 880, in _do_insert
    using=using, raw=raw)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\manager
.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\query.p
y", line 1125, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\sql\com
piler.py", line 1283, in execute_sql
    cursor.execute(sql, params)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\backends\utils
.py", line 100, in execute
    return super().execute(sql, params)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\backends\utils
.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._e
xecute)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\backends\utils
.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\backends\utils
.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\utils.py", lin
e 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\backends\utils
.py", line 85, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: ERRORE:  un valore chiave duplicato viola il vin
colo univoco "blog_post_pkey"
DETAIL:  La chiave (id)=(1) esiste già.

这里:

def save(self, *args, **kwargs):
    super(Post, self).save(*args, **kwargs)
    if self.title_it!='' and self.title_en!='' and self.text_it!='' and self.text_en!='' and self.tags!='':
        self.finished=True
        super(Post, self).save(*args, **kwargs)

您正在用同一个 kwarg 第二次呼叫 super().save()。因为 force_insert arg 设置为 True 正如您从回溯中看到的那样:

File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\query.py", line 521, in _create_object_from_params
    obj = self.create(**params)
File "D:\progetti\Envs\possedimenti\lib\site-packages\django\db\models\query.py", line 417, in create
    obj.save(force_insert=True, using=self.db)

你最终要求 ORM 创建第二条记录,因为那时 pk 已经设置(通过第一个 super.save() 调用),你确实违反了唯一约束。

您可以尝试开始使用 kwargs,但这实际上是个坏主意(最好将这些标志留给 ORM)- 简单的解决方案是确保您只调用 super.save() 一次:

def save(self, *args, **kwargs):
    # non-empty strings have a true value
    # so no need to explicitely test against
    # the empty string.
    # Note that this test will probably not
    # behave how you expect with strings containing
    # only space characters but that was your original
    # code behaviour too so I left this alone.
    self.finished = (
      self.title_it and self.title_en 
      and self.text_it and self.text_en 
      and self.tags
      )
    super(Post, self).save(*args, **kwargs)

编辑:由于 self.tags 实际上是一个相关字段(来自 taggit 应用程序),您无法无条件地测试 self.tags,因为您需要将实例保存在数据库中在您可以访问任何相关对象之前。这里的解决方案是先针对 self.pk 进行测试,然后仅在相关时测试其他字段:

def save(self, *args, **kwargs):
    if self.pk:
        self.finished = (
          self.title_it and self.title_en 
          and self.text_it and self.text_en 
          # unless `taggit` does some weird things
          # wrt/ tags storage, this should be the 
          # right test
          and self.tags.exists()
          )
    else:
        # no pk so no flags so it can not be finished
        self.finished = False

    # and call `super.save()` whatever the case
    super(Post, self).save(*args, **kwargs)