【问题标题】:How do I use a dictionary to update fields in Django models?如何使用字典更新 Django 模型中的字段?
【发布时间】:2011-07-27 02:40:25
【问题描述】:

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

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

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

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

【问题讨论】:

标签: python database django


【解决方案1】:

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

Book.objects.create(**d)

要更新现有模型,您需要使用 QuerySet filter 方法。假设您知道要更新的图书的pk

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

【讨论】:

  • 我认为 update 仅适用于 QuerySet 而不是单个对象。
  • 小心:update() 不尊重信号。请参阅下面@leech 的答案。
  • 这太棒了。
  • 嗯太糟糕了 update 和 update_or_create 不使用 using 参数,只有 save() 可以。
【解决方案2】:

使用** 创建新模型。遍历字典并使用setattr() 来更新现有模型。

来自 Tom Christie 的 Django Rest 框架

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()

【讨论】:

  • 嗯?能给我举个例子吗?那么,我将不得不编写一个循环遍历字典的自定义函数?
  • @TIMEX:请阅读。 docs.python.org/reference/expressions.html#calls 非常清楚这是如何工作的。
  • 您能解释一下为什么使用** 进行更新可能不是一个好主意吗?
  • @Pocin 使用 queryset.update(**fields) 根据 django 文档是一个好主意。引用:“如果你只是更新一条记录,不需要对模型对象做任何事情,最有效的方法是调用 update(),而不是将模型对象加载到内存中。”
  • 很好的答案,电话非常明确。不喜欢 filter(...).update(...) 因为搞砸过滤器会导致大量覆盖。
【解决方案3】:

如果你知道你想创建它:

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 函数,但我相信这不会发出任何信号(如果您不使用它们,这对您来说可能无关紧要)。但是,您可能不应该使用它来更改单个对象:

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

【讨论】:

  • 您的创建代码不会导致 2 次数据库命中吗?一个用于创建,另一个用于.save()?
  • 可以,但不保存。如果它已创建,将有一个查找 Book (SELECT),然后另一个更新它(将生成一个 UPDATE not INSERT 语句)。如果该书不存在,则可以查找(SELECT)并创建它(INSERT)。
  • that will not send any signals out: 怎么强调都不过分。
【解决方案4】:

如果你已经有 Django 对象并且你想更新它的字段,你可以在没有过滤器的情况下进行。因为你已经拥有了,在这种情况下,你可能会:

your_obj.__dict__.update(your_dict)
your_obj.save()

【讨论】:

  • 请加一个“.”:your_obj.__dict__.update(your_dict)
  • TLDR :不适用于对象字段。详细信息:如果您的字段不是str 等基本类型,int 即Entry 之类的对象。它不会起作用。
【解决方案5】:

要更新一条记录,您可以使用非常方便的功能:

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

    def update(self,*args, **kwargs):
            for name,values in kwargs.items():
                try:
                    setattr(self,name,values)
                except KeyError:
                    pass
            self.save()

然后:

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

【讨论】:

    【解决方案6】:

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

    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 方法具有有限且经过验证的字段集,而手动更新并非如此。

    【讨论】:

      猜你喜欢
      • 2019-09-30
      • 2020-08-13
      • 1970-01-01
      • 1970-01-01
      • 2017-08-26
      • 1970-01-01
      • 2015-09-14
      • 2021-11-19
      • 2017-06-15
      相关资源
      最近更新 更多