Двойное значение ключа нарушает уникальное ограничение на инициализацию базы данных
У меня есть модель для блога, где я хочу установить 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à.
1 ответ
Вот:
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)
Ты звонишь super().save()
во второй раз с тем же kwarg. так как 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)