14

是否可以使用 python 字典更新 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"
        }
    ]
})
4

6 回答 6

11

这里的游戏已经很晚了,但是 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()

唯一的区别update是你需要坚持一个id. 这样 mongoengine 就不会复制现有的文档id并更新它。

于 2018-10-25T19:11:25.687 回答
7

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

你这样称呼它update_document(document, data_dict)。它将遍历 的项目data_dict并使用 的键获取字段实例data_dict。然后它将调用字段实例field_value(field, value)在哪里。将使用并基于返回 MongoEngine 期望的值来检查字段的类型。例如,一个 normal 的值可以按原样返回,但对于一个,需要创建该嵌入文档类型的实例。它还对列表字段中的项目执行此技巧。fieldfield_value()field.__class__StringFieldEmbeddedDocumentField

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)
于 2013-09-25T14:30:48.097 回答
6

尝试更多类似的东西

p.update(**{
    "set__name": "Hank",
    "set__address": "Far away"
})
于 2013-09-25T11:32:34.477 回答
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

现在这个数据:

{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'}

哪个可以这样使用

document.update(**conv_dict_to_update(data))

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

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

于 2014-12-18T11:50:29.703 回答
1

这是使用 EmbeddedDocuments 更新文档的功能。它基于@rednaw 的解决方案,但考虑了具有 EmbeddedDocuments 的 EmbeddedDocuments。

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

这里的关键是field_value更新嵌入文档的方法,而不是用数据实例化它。

用法示例:

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'
        }
      ]
    }
  ]
})

仅供参考,这实际上是我猫的名字。

编辑:变量名中的错字。

于 2014-05-14T14:11:41.700 回答
0

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

结帐手册

包装标准 Python 字典的字典字段。这类似于嵌入文档,但结构没有定义。

于 2019-05-14T19:08:23.560 回答