使用python dict更新MongoEngine文档?
是否可以使用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
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没有用户友好的功能