【发布时间】:2019-09-21 01:37:39
【问题描述】:
我有模特
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
有一种类似question的解决方法:
将 primary_key 字段设置为 AutoField 后,问题就消失了。
但是,我的主键已经是 AutoField。
所以我用调试器逐步检查了 django 代码,并在文件 ...site-packages\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
似乎如果没有更改(因此 update 没有做任何事情),那么它将尝试插入重复记录。
我在get 之后添加了一个保存来测试这个:
device = DeviceAdmin.objects.get(uuid=uuid)
device.save()
它正在尝试插入一个副本。
这是否意味着我的代码需要跟踪一个对象是否必须保存到数据库?
解决这个问题的最简单方法是什么?
更新:
作为测试,我在找到带有get 的对象后立即放了一个save,没有任何更改:
device = DeviceAdmin.objects.get(uuid=uuid)
device.save()
Django 应该知道该对象是现有对象。然而,它会尝试插入一个副本。
同样,创建一个新对象并调用save两次:
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!
第二个save 尝试插入一个重复项 - 我可以看到我是否使用调试器逐步执行 django 代码。
【问题讨论】:
-
你的序列完全搞砸了吗? Possible duplicate
-
你试过这种方式吗(虽然我不确定它是否能解决):
device.save(update_fields=['field'])或者你可以使用device = DeviceAdmin.objects.filter(uuid=uuid).update(field=value) -
序列很好。我查过了。
-
您的诊断不正确。
updated标志只有在没有匹配的行时才会为 False,即 PK 不存在。如果 PK 存在但值设置为与现在相同,则不会为 False。 -
从你的另一个问题我知道你已经在数据库中有表。如果您使用
manage.py inspectdb为您的模型生成的代码,您能说明会发生什么吗?你可以随时回退到使用.save(force_update=True)
标签: django django-models