django update_or_create 得到“重复键值违反唯一约束”
Posted
技术标签:
【中文标题】django update_or_create 得到“重复键值违反唯一约束”【英文标题】:django update_or_create gets "duplicate key value violates unique constraint " 【发布时间】:2018-11-27 16:19:14 【问题描述】:也许我误解了 Django 的 update_or_create Model 方法的目的。
这是我的模型:
from django.db import models
import datetime
from vc.models import Cluster
class Vmt(models.Model):
added = models.DateField(default=datetime.date.today, blank=True, null=True)
creation_time = models.TextField(blank=True, null=True)
current_pm_active = models.TextField(blank=True, null=True)
current_pm_total = models.TextField(blank=True, null=True)
... more simple fields ...
cluster = models.ForeignKey(Cluster, null=True)
class Meta:
unique_together = (("cluster", "added"),)
这是我的测试:
from django.test import TestCase
from .models import *
from vc.models import Cluster
from django.db import transaction
# Create your tests here.
class VmtModelTests(TestCase):
def test_insert_into_VmtModel(self):
count = Vmt.objects.count()
self.assertEqual(count, 0)
# create a Cluster
c = Cluster.objects.create(name='test-cluster')
Vmt.objects.create(
cluster=c,
creation_time='test creaetion time',
current_pm_active=5,
current_pm_total=5,
... more simple fields ...
)
count = Vmt.objects.count()
self.assertEqual(count, 1)
self.assertEqual('5', c.vmt_set.all()[0].current_pm_active)
# let's test that we cannot add that same record again
try:
with transaction.atomic():
Vmt.objects.create(
cluster=c,
creation_time='test creaetion time',
current_pm_active=5,
current_pm_total=5,
... more simple fields ...
)
self.fail(msg="Should violated integrity constraint!")
except Exception as ex:
template = "An exception of type 0 occurred. Arguments:\n1!r"
message = template.format(type(ex).__name__, ex.args)
self.assertEqual("An exception of type IntegrityError occurred.", message[:45])
Vmt.objects.update_or_create(
cluster=c,
creation_time='test creaetion time',
# notice we are updating current_pm_active to 6
current_pm_active=6,
current_pm_total=5,
... more simple fields ...
)
count = Vmt.objects.count()
self.assertEqual(count, 1)
在最后一次 update_or_create 调用中,我收到此错误:
IntegrityError: duplicate key value violates unique constraint "vmt_vmt_cluster_id_added_c2052322_uniq"
DETAIL: Key (cluster_id, added)=(1, 2018-06-18) already exists.
为什么模型没有更新?为什么 Django 尝试创建违反唯一约束的新记录?
【问题讨论】:
update_or_create
包含过滤条件,在 defaults=..
中指定要更新的字段。
因此,对于我需要更新的每个字段,我需要在 defaults
中指定该字段。
【参考方案1】:
你应该分开你的领域:
-
应搜索的字段
应更新的字段
例如: 如果我有模型:
class User(models.Model):
username = models.CharField(max_length=200)
nickname = models.CharField(max_length=200)
我想搜索 username = 'Nikolas' 并将这个实例昵称更新为 'Nik'(如果没有用户名为 'Nikolas' 的用户,我需要创建它)我应该编写以下代码:
User.objects.update_or_create(
username='Nik',
defaults='nickname': 'Nikolas',
)
见https://docs.djangoproject.com/en/3.1/ref/models/querysets/
【讨论】:
你的例子中的defaults
不应该是字典吗?【参考方案2】:
上面已经很好地回答了这个问题。
为了更清楚,update_or_create() 方法应该有 **kwargs 作为您要通过过滤检查该数据是否已存在于 DB 中的参数。
select some_column from table_name where column1='' and column2='';
按 **kwargs 过滤将为您提供对象。现在,如果您希望更新这些过滤对象的任何数据/列,您应该在 update_or_create() 方法的默认参数中传递它们。
假设您现在基于过滤器找到了一个对象,默认参数值预计会被挑选和更新。
如果根据过滤器没有找到匹配的对象,那么它会继续创建一个带有过滤器的条目并传递默认参数。
【讨论】:
【参考方案3】:update_or_create(defaults=None, **kwargs
) 基本上有两个部分:
**kwargs
指定“过滤器”标准以确定此类对象是否已存在;和
defaults
是一个字典,其中包含映射到值的字段应该更新(以防我们发现这样的行)。
这里的问题是你的过滤器过于严格:你添加了几个过滤器,结果数据库找不到这样的行。那么会发生什么?然后数据库旨在使用这些过滤器值创建行(并且由于缺少defaults
,因此没有添加额外的值)。但事实证明,我们创建了一行,并且 cluster
和 added
的组合已经存在。因此数据库拒绝添加这一行。
所以这一行:
Model.objects.update_or_create(field1=val1,
field2=val2,
defaults=
'field3': val3,
'field4': val4
)
在语义上近似等于:
try:
item = Model.objects.get(field1=val1, field2=val2)
except Model.DoesNotExist:
Model.objects.create(field1=val1, field2=val2, field3=val3, field4=val4)
else:
item = Model.objects.filter(
field1=val1,
field2=val2,
).update(
field3 = val3
field4 = val4
)
(但原始调用通常在单个查询中完成)。
你可能应该这样写:
Vmt.objects.update_or_create(
cluster=c,
creation_time='test creaetion time',
defaults =
'current_pm_active': 6,
'current_pm_total': 5,
)
(或类似的)
【讨论】:
感谢您的清晰解释。我试图在我的单元测试中重现生产中将发生的事情。 Vmt 模型的数据来自我通过 URL 访问的 CSV 文件。如果此 cvs 文件中有新行,我想创建新的 Vmt 记录,但如果行已更改,我想更新读取 cvs 文件当天的 Vmt 记录。 @RedCricket:好吧,creation_time
可能是这里的“罪魁祸首”。就我个人而言,我认为将这些独特的东西放在一起有点问题,因为通常时间是不断增加的东西。所以这意味着有时你会创建一个副本,有时你不会。这是相当“不稳定”。
create_time 只是一个字符串,不属于对数据库表的任何约束。
@RedCricket:嗯,好吧,那么这应该可行。因此,如果已经存在具有给定cluster
和creation_time
的Vmt
,我们更新该行,否则我们创建一个。
模型中的 unique_together 指定 cluster
和 added
。我希望这将确保我在给定的一天只有一组给定集群的 Vmt 数据。以上是关于django update_or_create 得到“重复键值违反唯一约束”的主要内容,如果未能解决你的问题,请参考以下文章
django单表操作中update_or_create不能更新多于一个数据的信息
django update_or_create 得到“重复键值违反唯一约束”
Django 2021年最新版教程17数据库操作 models 存在更新 不存在新建update_or_create
是否有用于使用 VTL 的 AWS Appsync 的 Model.objects.update_or_create()?