如何更新 django 模型实例的多个字段?

Posted

技术标签:

【中文标题】如何更新 django 模型实例的多个字段?【英文标题】:How to update multiple fields of a django model instance? 【发布时间】:2010-12-07 07:03:14 【问题描述】:

我想知道,在 django 中更新模型实例的多个字段的标准方法是什么? ...如果我有一个包含某些字段的模型,

Class foomodel(models.Model):
    field1 = models.CharField(max_length=10)
    field2 = models.CharField(max_length=10)
    field3 = models.CharField(max_length=10)
    ...

...我用给定的一个字段实例化它,然后在一个单独的步骤中我想提供其余字段,我该如何通过仅传递字典或键值参数来做到这一点?可能吗?

换句话说,假设我有一个包含一些数据的字典,其中包含我想要写入该模型实例的所有内容。模型实例已在单独的步骤中实例化,假设它尚未持久化。我可以为每个字段说foo_instance.field1 = my_data_dict['field1'],但有些东西告诉我应该有一种方法可以在模型实例上调用一个方法,我只需一次传递所有字段-值对并更新它们。像foo_instance.update(my_data_dict) 这样的东西。我没有看到任何这样的内置方法,我错过了它还是如何有效地完成?

我觉得这是一个明显的 RTM 类问题,但我只是没有在文档中看到它。

【问题讨论】:

【参考方案1】:

__dict__ 很容易混淆,但这不适用于从父类继承的属性。

您可以遍历 dict 以分配给对象:

for (key, value) in my_data_dict.items():
    setattr(obj, key, value)
obj.save()

或者您可以直接从查询集修改它(确保您的查询集只返回您感兴趣的对象):

FooModel.objects.filter(whatever="anything").update(**my_data_dict)

【讨论】:

谢谢@Wilfred。很有帮助。对于其他人:不要忘记在 for 循环之后执行 obj.save() 请注意FooModel.objects.filter(whatever="anything").update(**my_data_dict) 会绕过保存方法。您最终可能会保存应该引发异常的东西。【参考方案2】:

你可以试试这个:

obj.__dict__.update(my_data_dict)

【讨论】:

这不适用于继承的属性。 它不适用于继承的属性意味着什么? 另外,您是否正在尝试使用 dict 更新 ForeignKey,相关值名称是 field_name_id 而不是 field_name,您需要小心。 这没有保留我的记录...我可能会丢失什么? @tomascharad - 你必须像 obj.__dict__.update(my_data_dict) 然后 obj.save() 那样调用它来保存数据。【参考方案3】:

这似乎是您想要做的一件很自然的事情,但和您一样,我也没有在文档中找到它。文档确实说您应该在模型上对 save() 进行子类化。这就是我所做的。

def save(self, **kwargs):
    mfields = iter(self._meta.fields)
    mods = [(f.attname, kwargs[f.attname]) for f in mfields if f.attname in kwargs]
    for fname, fval in mods: setattr(self, fname, fval)
    super(MyModel, self).save()

【讨论】:

考虑为模型添加一个新方法“dict_save”(因此模型的 2 个保存方法 - 普通和 dict)与主体在下次我需要这个时。我查看了我用来执行此操作的代码,除了我在保存之外执行此操作之外,它是相同的。有一个方法 update_model_obj_from_dict(model_object, update_dict) 与上面的主体几乎相同,并期望我会选择何时调用 save() 之后。我有点喜欢有选择。所以也许模型上有一个名为“set_from_dict”的方法,上面除了最后一行之外的所有内容,稍后再使用保存。【参考方案4】:

我得到主键的名字,用它来过滤Queryset.filter()并用Queryset.update()更新。

fooinstance = ...    
# Find primary key and make a dict for filter
pk_name foomodel._meta.pk.name
filtr = pk_name: getattr(fooinstance, pk_name)
# Create a dict attribute to update
updat = 'name': 'foo', 'lastname': 'bar'
# Apply
foomodel.objects.filter(**filtr).update(**updat)

这让我可以更新一个实例,不管主键是什么。

【讨论】:

【参考方案5】:

使用update()更新

Discussion.objects.filter(slug=d.slug)
    .update(title=form_data['title'],
            category=get_object_or_404(Category, pk=form_data['category']),
            description=form_data['description'], closed=True)

【讨论】:

欢迎来到 SO。发布答案时,请附上您的代码说明,并记住格式化文本。 需要注意的是,这不会调用 save() 方法,也不会调用 pre_save/post_save 信号。 @Maxim 有没有办法手动触发这些信号,因为从未调用过 save() 方法? @enchance 您必须在每个实例上调用 save 方法,因为 update 方法不会调用它,也不会发送 pre/post 保存信号。 docs.djangoproject.com/en/3.0/ref/models/querysets/#update

以上是关于如何更新 django 模型实例的多个字段?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Django 更新模型实例

Django-如何在一个字段中添加同一模型的多个实例

如何使用 Django Rest Framework 创建多个模型实例?

使用 django 模型实例更新项目

更新实例时Django表示字段初始值

Django:如何使用 pre_save 信号更新模型实例?