是否可以使用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"
}
]
})
答案 0 :(得分:7)
好的,我刚刚为它做了一个功能。
你称之为update_document(document, data_dict)
。它将循环遍历data_dict
项,并使用data_dict
的键获取字段实例。然后,它将调用field_value(field, value)
,其中field
是字段实例。 field_value()
将使用field.__class__
检查字段类型,并根据该返回值查看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)
答案 1 :(得分:4)
尝试更像这样的东西
p.update(**{
"set__name": "Hank",
"set__address": "Far away"
})
答案 2 :(得分:3)
我已经尝试了上面的大多数答案,它们似乎都不适用于嵌入式文档。即使他们更新了字段,他们也删除了嵌入式文档中未填充字段的内容。
为此,我决定采用@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
我不知道这有多有效但是有效。
答案 3 :(得分:2)
这里的游戏还算晚,但是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
复制文档,而是对其进行更新。
答案 4 :(得分: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'
}
]
}
]
})
仅供参考这实际上是我猫的名字。
编辑:变量名中的拼写错误。
答案 5 :(得分:0)
要将python dict存储为子文档,可以使用mongoengine.fields.DictField
。
结帐manuals。
包装标准Python字典的字典字段。这是 类似于嵌入式文档,但未定义结构。