Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/60.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用python dict更新MongoEngine文档?_Python_Mongodb_Mongoengine - Fatal编程技术网

使用python dict更新MongoEngine文档?

使用python dict更新MongoEngine文档?,python,mongodb,mongoengine,Python,Mongodb,Mongoengine,是否可以使用python dict更新MongoEngine文档 例如: class Pets(EmbeddedDocument): name = StringField() class Person(Document): name = StringField() address = StringField() pets = ListField(EmbeddedDocumentField(Pets)) p = Person() p.update_with_di

是否可以使用python dict更新MongoEngine文档

例如:

class Pets(EmbeddedDocument):
    name = StringField()

class Person(Document):
    name = StringField()
    address = StringField()
    pets = ListField(EmbeddedDocumentField(Pets))

p = Person()
p.update_with_dict({
    "name": "Hank",
    "address": "Far away",
    "pets": [
        {
            "name": "Scooter"
        }
    ]
})

试试这样的

p.update(**{
    "set__name": "Hank",
    "set__address": "Far away"
})
document.update(**conv_dict_to_update(data))

好的,我刚刚为它做了一个函数

你把它叫做
update\u document(document,data\u dict)
。它将循环遍历
data\u dict
项,并使用
data\u dict
键获取字段实例。然后它将调用
field\u value(field,value)
,其中
field
是字段实例
field\u value()
将使用
field来检查字段的类型。\uuu class\uuu
并基于此返回MongoEngine期望的值。例如,普通
StringField
的值可以按原样返回,但对于
EmbeddedDocumentField
,需要创建该嵌入文档类型的实例。它还对列表字段中的项执行此操作

from mongoengine import fields


def update_document(document, data_dict):

    def field_value(field, value):

        if field.__class__ in (fields.ListField, fields.SortedListField):
            return [
                field_value(field.field, item)
                for item in value
            ]
        if field.__class__ in (
            fields.EmbeddedDocumentField,
            fields.GenericEmbeddedDocumentField,
            fields.ReferenceField,
            fields.GenericReferenceField
        ):
            return field.document_type(**value)
        else:
            return value

    [setattr(
        document, key,
        field_value(document._fields[key], value)
    ) for key, value in data_dict.items()]

    return document
用法:

class Pets(EmbeddedDocument):
    name = StringField()

class Person(Document):
    name = StringField()
    address = StringField()
    pets = ListField(EmbeddedDocumentField(Pets))

person = Person()

data = {
    "name": "Hank",
    "address": "Far away",
    "pets": [
        {
            "name": "Scooter"
        }
    ]
}

update_document(person, data)

这是一个使用嵌入文档更新文档的函数。它基于@rednaw的解决方案,但考虑到嵌入文档具有嵌入文档

from mongoengine.fields import *

def field_value(field, value):
  ''' 
  Converts a supplied value to the type required by the field.
  If the field requires a EmbeddedDocument the EmbeddedDocument
  is created and updated using the supplied data.
  '''
  if field.__class__ in (ListField, SortedListField):
    # return a list of the field values 
    return [
      field_value(field.field, item) 
      for item in value]

  elif field.__class__ in (
    EmbeddedDocumentField,
    GenericEmbeddedDocumentField,
    ReferenceField,
    GenericReferenceField):

    embedded_doc = field.document_type()
    update_document(embedded_doc, value)
    return embedded_doc
  else:
    return value


def update_document(doc, data):
  ''' Update an document to match the supplied dictionary.
  '''
  for key, value in data.iteritems():

    if hasattr(doc, key):
        value = field_value(doc._fields[key], value)
        setattr(doc, key, value)
    else:
        # handle invalid key
        pass

  return doc
这里的关键是更新嵌入文档而不是用数据实例化它的
字段\u值
方法

用法示例:

class Pets(EmbeddedDocument):
    name = StringField()

class Person(EmbeddedDocument):
    name = StringField()
    address = StringField()
    pets = ListField(EmbeddedDocumentField(Pets))

class Group(Document):
    name = StringField()
    members = ListField(EmbeddedDocumentField(Person))

g = Group()

update_document(g, {
  'name': 'Coding Buddies',
  'members': [
    {
      'name': 'Dawson',
      'address': 'Somewhere in Nova Scotia',
      'pets': [
        {
          'name': 'Sparkles'
        }
      ]
    },
    {
      'name': 'rednaw',
      'address': 'Not too sure?',
      'pets': [
        {
          'name': 'Fluffy'
        }
      ]
    }
  ]
})
告诉你,那实际上是我的猫的名字


编辑:输入变量名。

我已经尝试了上面的大多数答案,似乎没有一个能真正用于嵌入文档。即使他们更新了字段,他们也删除了嵌入文档中未填充字段的内容

为此,我决定采用@hckjck建议的路径,我编写了一个简单的函数,将dict转换为一种格式,以便
文档可以处理它。update

def convert_dict_to_update(dictionary, roots=None, return_dict=None):
    """    
    :param dictionary: dictionary with update parameters
    :param roots: roots of nested documents - used for recursion
    :param return_dict: used for recursion
    :return: new dict
    """
    if return_dict is None:
        return_dict = {}
    if roots is None:
        roots = []

    for key, value in dictionary.iteritems():
        if isinstance(value, dict):
            roots.append(key)
            convert_dict_to_update(value, roots=roots, return_dict=return_dict)
            roots.remove(key)  # go one level down in the recursion
        else:
            if roots:
                set_key_name = 'set__{roots}__{key}'.format(
                    roots='__'.join(roots), key=key)
            else:
                set_key_name = 'set__{key}'.format(key=key)
            return_dict[set_key_name] = value

    return return_dict
现在这些数据:

{u'communication': {u'mobile_phone': u'2323232323', 'email':{'primary' : 'email@example.com'}}}
将转换为:

{'set__communication__mobile_phone': u'2323232323', 'set__communication__email__primary': 'email@example.com'}
可以这样使用

p.update(**{
    "set__name": "Hank",
    "set__address": "Far away"
})
document.update(**conv_dict_to_update(data))
本要点中还提供:


我不知道这有多有效,但它确实有效。

游戏进行得很晚,但是FWIW,MongoEngine有一个内置的解决方案

无论您想
创建
还是
更新
,都可以执行以下操作:

class Pets(EmbeddedDocument):
    name = StringField()

class Person(Document):
    name = StringField()
    address = StringField()
    pets = ListField(EmbeddedDocumentField(Pets))

p = Person(**{
    "name": "Hank",
    "address": "Far away",
    "pets": [{"name": "Scooter"}]
})
p.save()

update
的唯一区别是您需要插入一个
id
。这样,mongoengine就不会用现有的
id复制文档,而是更新它。

要将python dict存储为子文档,可以使用
mongoengine.fields.DictField

结帐

包装标准Python字典的字典字段。这是 与嵌入文档类似,但未定义结构


谢谢你的回答。这适用于普通字段,但不适用于EmbeddedDocumentFields。我在问题中增加了要求。抱歉,现在不清楚。您可以通过更新查询中的列表项索引
p.update(**{“set\u name”:“Hank”,“set\u address”:“Far away”,“set\u pets\u 0\u name”:“Scooter})
@kovan您到底是什么意思?当您尝试更新已删除的字段时?如果你需要一个后备方案,你可以自己写,不应该太难。你能更清楚你的问题吗。通过阅读您下面的答案,可以清楚地看出您正在寻找一种允许您更新嵌入文档的解决方案,尽管这应该在问题中说明。@Dawson否,我想使用Python dict更新完整的文档,包括嵌入文档/列表字段等。您的意思不是更新,而是
创建
p=Person()
使用默认值实例化对象,不会从数据库中获取现有对象,然后更新属性。嘿,道森,我的方法到底有什么不适用于你?@rednaw你的解决方案没有考虑包含嵌入文档的嵌入文档。我正在更新的模型有一个嵌入文档的列表字段。这些嵌入文档本身有一个嵌入文档的列表字段。您的解决方案允许更新EmbeddedDocuments、ListFields和包含EmbeddedDocuments的ListFields,但不允许更新包含EmbeddedDocuments或ListFields的EmbeddedDocuments。Dawson,您的使用示例似乎与我的方法配合得很好。我不明白什么不适合你。我用这个脚本进行了测试:不确定这个函数,有几个未定义的变量。。。例如更新实体和最后一行的实体…这是一种类型。我在编辑中修复了这个问题。实体应该是文档。这是新的解决方案。当您的文档模型中有主键或唯一约束时,此解决方案将失败。您在这方面帮了我很多忙,很遗憾,mongoengine没有用户友好的功能