django update_or_create 得到 "duplicate key value violates unique constraint "

django update_or_create gets "duplicate key value violates unique constraint "

也许我误解了 Django 的 update_or_create 模型方法的目的。

这是我的模型:

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:\n{1!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 尝试创建一条违反唯一约束的新记录?

你应该分开你的领域:

  1. 应搜索的字段
  2. 应更新的字段

例如: 如果我有型号:

class User(models.Model):
    username = models.CharField(max_length=200)
    nickname = models.CharField(max_length=200)

我想搜索用户名 = 'Nikolas' 并将此实例昵称更新为 'Nik'(如果没有用户名 'Nikolas' 我需要创建它)我应该写这个代码:

User.objects.update_or_create(
    username='Nikolas', 
    defaults={'nickname': 'Nik'},
)

参见https://docs.djangoproject.com/en/3.1/ref/models/querysets/

update_or_create(defaults=None, **kwargs) 基本上有两部分:

  1. **kwargs 指定“filter”标准以确定此类对象是否已经存在;和
  2. defaults 这是一个字典,其中包含映射到我们 创建 新行时应该使用的值的字段(以防过滤找不到一行),或者应该更新哪些值(以防我们找到这样的行)。

这里的问题是您使 过滤器 过于严格:您添加了多个过滤器,结果数据库找不到这样的行。那么会发生什么?然后数据库旨在创建具有这些过滤值的行(并且由于缺少 defaults,因此不会添加额外的值)。但事实证明我们创建了一行,并且 clusteradded 的组合已经存在。因此数据库拒绝添加这一行。

所以这一行:

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,
    }
)

(或类似的东西)

上面已经回答的很好

为了更清楚,update_or_create() 方法应该将 **kwargs 作为您要通过过滤检查该数据是否已存在于数据库中的参数。

select some_column 来自 table_name 其中 column1='' 和 column2='';

按 **kwargs 过滤将为您提供对象。现在,如果您希望更新这些过滤对象中的任何 data/column,您应该将它们传递给 update_or_create() 方法中的默认参数。

假设您找到了一个基于过滤器的对象,现在应该选择和更新默认参数值。

如果没有根据过滤器找到匹配的对象,那么它将继续并创建一个带有过滤器的条目并传递默认参数。