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

詹戈尝试插入而不是更新保存()

葛嘉悦
2023-03-14

我有一个模型

class DeviceAdmin(models.Model):
    dev_id = models.AutoField(db_column='dev_id', primary_key=True)
...

视图定位对象:

device = DeviceAdmin.objects.get(uuid=uuid)

然后它会做出一些改变(或者可能不会)

if (...):
    device.os_type = 'windows'
...

然后它尝试保存任何更改:

device.save()

这里是Django尝试插入另一行而不是更新的地方,导致了一个DB错误

django.db.utils.IntegrityError: duplicate key value violates unique constraint 

解决方案中有一种类似的问题:

一旦我将primary_key字段设置为自动字段,问题就消失了。

但是,我的主键已经是一个自动字段。

所以我用调试器逐步浏览了django代码,并在文件中资助了这个...站点包\django\models\base.py

    # If possible, try an UPDATE. If that doesn't update anything, do an INSERT.
    if pk_set and not force_insert:
        base_qs = cls._base_manager.using(using)
        values = [(f, None, (getattr(self, f.attname) if raw else f.pre_save(self, False)))
                  for f in non_pks]
        forced_update = update_fields or force_update
        updated = self._do_update(base_qs, using, pk_val, values, update_fields,
                                  forced_update)
        if force_update and not updated:
            raise DatabaseError("Forced update did not affect any rows.")
        if update_fields and not updated:
            raise DatabaseError("Save with update_fields did not affect any rows.")
    if not updated:
        if meta.order_with_respect_to:
            # If this is a model with an order_with_respect_to
            # autopopulate the _order field
            field = meta.order_with_respect_to
            filter_args = field.get_filter_kwargs_for_object(self)
            order_value = cls._base_manager.using(using).filter(**filter_args).count()
            self._order = order_value

        fields = meta.local_concrete_fields
        if not pk_set:
            fields = [f for f in fields if f is not meta.auto_field]

        update_pk = meta.auto_field and not pk_set
        result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
        if update_pk:
            setattr(self, meta.pk.attname, result)
    return updated

似乎如果没有更改(因此更新不做任何事情),那么它将尝试插入重复记录。

我通过在< code>get之后添加一个保存来测试这一点:

device = DeviceAdmin.objects.get(uuid=uuid)
device.save()

它试图插入一个副本。

这是否意味着,我的代码需要跟踪一个对象是否必须保存到DB?

解决这个问题的最简单方法是什么?

更新:

作为一个测试,在用< code>get找到一个对象后,我立即执行< code>save,而不做任何更改:

device = DeviceAdmin.objects.get(uuid=uuid)
device.save()

Django应该知道这个对象是一个已经存在的对象。然而,它试图插入一个副本。

同样,创建一个新对象并调用保存两次:

device = DeviceAdmin(...) # create a new object (it has null `dev_id`)
device.save()             # insert the new object into DB; get 'dev_id'
                          #   for the newly inserted record
device.save()             # This shouldn't do anything!

第二次< code>save尝试插入一个副本——正如我可以看到的,如果我用调试器单步执行django代码。

共有3个答案

岑彬炳
2023-03-14

可能发生这种情况的一个原因是如果您的主键字段使用默认值定义。我在您的示例中没有看到默认值,但我也没有看到数据类型是uuid,所以我想我会提供这个答案以防万一。

如果您的模型的pkey如下所示:models.UUIDField(primary_key=True,默认=uuid.uuid4),调用保存将始终导致INSERT而不是UPDATE。我相信即使您将force_update=True传递给保存方法,这也是真的。这曾经作为错误提交,但实际上是Django 3根据文档的预期行为。

如果要使用 uuid 作为主键,有几种方法可以解决此问题。最好的方法可能是遵循文档的建议,并使用更新或update_or_create方法来更新模型实例。

>>> MyModel.objects.update_or_create(pk=existing_pk, defaults={'name': 'new name'})
>>> MyModel.objects.filter(pk=existing_pk).update(name='new name')

或者,您也可以从主键定义中删除默认参数,并通过覆盖模型的save方法来确保每个实例在保存时都有一个分配的uuid例如:

def save(self, *args, **kwargs):
    if not self.id:
        self.id = uuid.uuid1()
        super(DeviceAdmin, self).save(*args, **kwargs)
郎鸿
2023-03-14

在我的例子中,我在班上有一个独特的CharField。在我看来,Django有时会将它视为主键,有时不会——结果是有时没有意识到它应该是更新而不是插入。

添加显式的id=AutoField(primary_key=True)为我解决了问题。

冉锋
2023-03-14

您在字段定义方面肯定有一些问题(可能出于某种原因,您应该添加< code>unique=True)。但是如果您仍然不能确定更新失败的原因(如果< code>UPDATE返回“no rows updated”,则发生< code>INSERT),那么您可以使用< code >。保存(force_update=True)作为最后的手段。

此外,您的代码中存在一些不一致:

  • 字段被命名为dev_id但您在筛选中使用 uuid.get(uuid=uuid))。是否确实为primary_key字段设置了名称?
  • AutoField假设使用整数字段,因此在get_prep_value期间,它会执行int(self.pk),其工作而不会在uuid实例上抛出任何错误。UUID,但如果 db 内部使用 varchar 字段,则它将被转换回具有错误值的 str(例如 str(int(uuid.UUID('579aea21-49a4-4819-90ec-a192014b4a44')) == “116447198070669671892400511866020514372”)。所以这就是可能导致问题的原因。尝试使用 UUID 字段或字符字段,而不是自动字段
import uuid


class SomeModel(models.Model): 

    dev_id = models.UUIDField(primary_key=True, blank=True, default=uuid.uuid4)

# Or if your column is a varchar:

class SomeModel(models.Model): 

    def dev_id_default():
        return uuid.uuid4().hex  # or str(uuid.uuid4()) if you need values with `-`

    dev_id = models.CharField(primary_key=True, blank=True, default=dev_id_default, max_length=33_OR_36)

看起来您正在使用数据库中预先存在的表:尝试使用python manage.py inspectdb生成的模型的代码。

 类似资料:
  • 嗨,我是Spring Data JPA的新手,我想知道即使我将Id传递给实体,Spring data jpa也是插入而不是合并的。我想当我实现持久接口并实现这两个方法时: 它将自动合并而不是持久化。 我有一个名为User like的实体类: 再上一节课 我有一个名为UserRepostory的自定义存储库,它扩展了JpaReopistory。当我看到实现演示SpringDataJpa使用以上两种方

  • 问题内容: 我有一个简单的测试,尝试在其中更新对象,但是合并似乎在执行插入操作而不是更新操作。 } 这是我的更新方法 更新代码 现在,我收到以下错误无法提交JPA事务;嵌套的异常是javax.persistence.RollbackException:标记为rollbackOnly的事务 问题答案: 我有一个版本列,当种子数据插入数据库时​​未设置该列。因此,所有有关更新和删除的问题

  • 问题内容: 编辑。在扩展基本存储库类并添加insert方法的同时,一个更优雅的解决方案似乎是在实体中实现Persistable。查看 可能的解决方案2 我正在使用Hibernate作为ORM 创建服务。 遵循本教程的基础。 http://www.petrikainulainen.net/spring-data-jpa- tutorial/ 我的实体存储库扩展 我正在使用使用有意义的主键而不是自动生

  • 当试图更新现有的Django模型对象时(使用< code>save()方法),会插入一个新行。 例如: 在第二次调用< code>save()方法之后,一个重复的条目被插入到我的表中。 以下是模型定义的示例:

  • Spring Boot 2.4.0,DB是MySql 8。 使用REST每15秒从远程获取数据并使用将其存储到MySql DB。 它为所有给定的实体调用save()方法。 所有数据都设置了ID。 我希望如果DB中没有这样的id-它将被插入。 如果这样的ID已经在DB中呈现-它将被更新。 这里是从控制台剪下的: 下面是获取和保存的方法,如下所示: 此方法每15秒运行一次。 实体: 和存储库: 这是以

  • 我有一个地址对象,它具有城市属性。创建要通过EF6插入的全新地址时,我会填写所有必需的基本地址属性(地址行1、邮政编码等),但我不需要完全水合的城市实例,只要它具有如下ID: 当我尝试插入我的地址时,它也会尝试对城市的属性进行验证,但我不想对城市进行任何CRUD,因为我只需要它的ID。 我发现了下面的问题,它引导我从DbContext中分离条目,这样EF就不会试图对所述对象执行CRUD: 如何阻止