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


69

我想知道,在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问题,但我只是在文档中没有看到。

Answers:


139

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

您可以遍历字典以分配给该对象:

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

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

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

1
谢谢@Wilfred。很有帮助。对于其他人:不要忘记obj.save()在for循环之后做
Anupam

3
注意FooModel.objects.filter(whatever="anything").update(**my_data_dict)绕过保存方法。您可能最终会保存应该引发异常的内容。
卡尔·布鲁贝克

34

您可以尝试以下方法:

obj.__dict__.update(my_data_dict)

9
这不适用于继承的属性。
威尔弗雷德·休斯

3
不适用于继承属性的事实有什么含义?
Brian Peterson

另外,如果您要尝试使用dict更新ForeignKey,则相关值名称field_name_id不是field_name,则需要注意这一点。
levi 2015年

这并没有保留我的记录...我可能会丢失什么?
tomascharad'1

6
@tomascharad-您必须先调用它,obj.__dict__.update(my_data_dict)然后obj.save()才能保留数据。
Michael B

4

您似乎想做一件很自然的事情,但是像您一样,我也没有在文档中找到它。文档确实说您应该在模型上子类化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()

1
我想为模型制作一个装饰器,以在下次需要时在主体上方添加新方法“ dict_save”(因此为模型提供2种保存方法-normal和dict)。我查找了用于执行此操作的代码,除了在保存之外执行此操作外,它是相同的。有一个方法update_model_obj_from_dict(model_object,update_dict)与上面的主体几乎相同,并且希望我选择何时在之后调用save()。我有点喜欢选择。因此,也许在模型上的名为“ set_from_dict”的方法具有除最后一行以外的所有其他内容,并稍后使用save。
Purrell 2012年

3

我得到主键的名称,用它来过滤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)

这使我无论主键如何都可以更新实例。


3

使用更新 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。发布答案时,请附上代码说明,并记住格式化文本。
托尼

2
重要的是要注意,这不会调用save()方法或pre_save / post_save信号。
Maxim

@Maxim有没有办法手动触发这些信号,因为save()从未调用过该方法?
附魔

@enchance您必须在每个实例上调用save方法,因为update方法既不调用它也不发送前/后保存信号。docs.djangoproject.com/en/3.0/ref/models/querysets/#update
Maxim
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.