使用python dict更新MongoEngine文档?

git*_*rik 11 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_dict({
    "name": "Hank",
    "address": "Far away",
    "pets": [
        {
            "name": "Scooter"
        }
    ]
})
Run Code Online (Sandbox Code Playgroud)

git*_*rik 7

好的,我刚刚为它做了一个功能.

你称之为update_document(document, data_dict).它将循环遍历项目data_dict并使用密钥获取字段实例data_dict.然后,它会调用field_value(field, value)这里field是该领域的实例.field_value()将检查字段的类型,field.__class__并根据该返回值查看MongoEngine期望的值.例如,StringField可以按原样返回法线的值,但是对于a 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
Run Code Online (Sandbox Code Playgroud)

用法:

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)
Run Code Online (Sandbox Code Playgroud)


Fyd*_*ydo 7

在这里玩游戏很晚,但是FWIW,MongoEngine为此提供了内置解决方案。

不管你想create还是update可以做到以下几点:

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()
Run Code Online (Sandbox Code Playgroud)

唯一的区别update是您需要坚持使用id。这样,mongoengine不会复制现有文档id并更新它。

  • 这是新的解决方案。 (2认同)
  • 投票大有帮助;)@GeorgZimmer (2认同)

hck*_*jck 6

试试更多这样的东西

p.update(**{
    "set__name": "Hank",
    "set__address": "Far away"
})
Run Code Online (Sandbox Code Playgroud)

  • 您可以在更新查询`p.update(** {“ set__name”:“汉克”,“ set__address”:“遥远”,'set__pets__0__name':'Scooter'})中完成列表项索引。 (2认同)

Vis*_*oru 6

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

为此,我决定采用@hckjck 建议的路径,我编写了一个简单的函数,将 dict 转换为格式,以便可以通过以下方式处理document.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
Run Code Online (Sandbox Code Playgroud)

现在这个数据:

{u'communication': {u'mobile_phone': u'2323232323', 'email':{'primary' : 'email@example.com'}}}
Run Code Online (Sandbox Code Playgroud)

将转换为:

{'set__communication__mobile_phone': u'2323232323', 'set__communication__email__primary': 'email@example.com'}
Run Code Online (Sandbox Code Playgroud)

哪个可以这样使用

document.update(**conv_dict_to_update(data))
Run Code Online (Sandbox Code Playgroud)

也可在此要点中找到:https : //gist.github.com/Visgean/e536e466207bf439983a

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