Обновить документ MongoEngine с помощью python dict?
Возможно ли обновить документ MongoEngine с помощью python dict?
Например:
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"
}
]
})
Ответы
Ответ 1
Хорошо, я просто сделал для этого функцию.
Вы называете его как 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)
Ответ 2
Попробуйте что-нибудь еще
p.update(**{
"set__name": "Hank",
"set__address": "Far away"
})
Ответ 3
Я пробовал большинство ответов выше, никто из них, похоже, действительно не работает с встроенными документами. Несмотря на то, что они обновили поля, они также удалили содержимое незаполненных полей в документе Embedded.
Для этого я решил пойти по пути, предложенному @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 protected]'}}}
будет преобразован в:
{'set__communication__mobile_phone': u'2323232323', 'set__communication__email__primary': '[email protected]'}
Что можно использовать как это
document.update(**conv_dict_to_update(data))
Также доступно в этом документе: https://gist.github.com/Visgean/e536e466207bf439983a
Я не знаю, насколько это эффективно, но это работает.
Ответ 4
Вот функция обновления документов с помощью 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'
}
]
}
]
})
FYI Это на самом деле мое имя кошки.
EDIT: опечатка в именах переменных.
Ответ 5
Довольно поздно к игре здесь, но 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
и обновит его.
Ответ 6
Для хранения python dict в качестве mongoengine.fields.DictField
документа можно использовать mongoengine.fields.DictField
.
Оплата пособия.
Поле словаря, которое содержит стандартный словарь Python. Это похоже на внедренный документ, но структура не определена.