如何使用字典更新Django模型中的字段?


77

假设我有一个这样的模型:

class Book(models.Model):
    num_pages = ...
    author = ...
    date = ...

我可以创建字典,然后使用它插入或更新模型吗?

d = {"num_pages":40, author:"Jack", date:"3324"}

2
是。尝试一下。**在Python语言参考手册中查找运算符。 docs.python.org/reference/expressions.html#calls
美国洛特

Answers:


101

这是使用字典d创建的示例:

Book.objects.create(**d)

要更新现有模型,您将需要使用QuerySetfilter方法。假设您知道pk要更新的书的:

Book.objects.filter(pk=pk).update(**d)

19
我认为更新仅适用于QuerySet而不适用于单个对象。
Thierry Lam

13
注意:update()不尊重信号。请参阅下面@leech的答案。
Wtower

太好了
joshlsullivan

61

如果您知道要创建它:

Book.objects.create(**d)

假设您需要检查现有实例,则可以使用get或create找到它:

instance, created = Book.objects.get_or_create(slug=slug, defaults=d)
if not created:
    for attr, value in d.items(): 
        setattr(instance, attr, value)
    instance.save()

如另一个答案中所述,您也可以update在queryset Manager上使用该函数,但我相信不会发送任何信号(如果您不使用它们,则对您而言可能并不重要)。但是,您可能不应该使用它来更改单个对象:

Book.objects.filter(id=id).update()

您的创建代码不会导致2次数据库命中吗?一个用于创建,另一个用于.save()
豪尔赫·雷涛2014年

2
是的,但不能保存。如果已创建,将有一个要查找Book(SELECT),然后有一个要更新Book(将生成UPDATE not INSERT语句)。如果该书不存在,则可以进行查找(选择)并创建(插入)。
ech

7
that will not send any signals out:不能充分强调这一点。
Wtower

58

使用**用于创建一个新的模式。遍历字典并使用setattr()以更新现有模型。

摘自Tom Christie的Django Rest Framework

https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/serializers.py

for attr, value in validated_data.items():
    setattr(instance, attr, value)
instance.save()

1
??你能给我举个例子吗?因此,我将不得不编写一个遍历字典的自定义函数?
TIMEX

1
@TIMEX:请阅读。 docs.python.org/reference/expressions.html#calls非常清楚其工作原理。
S.Lott

1
您能解释一下为什么使用**更新可能不是一个好主意吗?
罗宾·内默斯

queryset.update(**fields)根据Django文档,@Pocin使用是一个好主意。Quote:“如果您只是更新记录,而无需对模型对象做任何事情,最有效的方法是调用update(),而不是将模型对象加载到内存中。”
CrowbarKZ

2
好的答案,电话非常明确。不喜欢filter(...)。update(...)的迷,因为将过滤器弄糟会导致大量覆盖。
科比·杰克逊·杰克逊

1

如果您已经有了Django对象,并且想要更新它的字段,则可以不使用过滤器来执行。因为您已经拥有了,在这种情况下,可能会:

your_obj.__dict__update(your_dict)
your_obj.save()

0

除了其他答案之外,这里还有一个更安全的版本,可防止混淆相关字段:

def is_simple_editable_field(field):
    return (
            field.editable
            and not field.primary_key
            and not isinstance(field, (ForeignObjectRel, RelatedField))
    )

def update_from_dict(instance, attrs, commit):
    allowed_field_names = {
        f.name for f in instance._meta.get_fields()
        if is_simple_editable_field(f)
    }

    for attr, val in attrs.items():
        if attr in allowed_field_names:
            setattr(instance, attr, val)

    if commit:
        instance.save()

它检查您要更新的字段是否可编辑,不是主键,也不是相关字段之一。

用法示例:

book = Book.objects.first()
update_from_dict(book, {"num_pages":40, author:"Jack", date:"3324"})

DRF串行器.create.update方法的豪华之处在于,字段的集合有限且经过验证,而手动更新则不是这种情况。

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.