14

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"
        }
    ]
})
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)である where isを呼び出します。を使用してフィールドのタイプをチェックし、それに基づいて、MongoEngine が期待する値を返します。たとえば、法線の値はそのまま返すことができますが、 の場合は、埋め込まれたドキュメント タイプのインスタンスを作成する必要があります。また、リスト フィールドのアイテムに対してもこのトリックを行います。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

上記のほとんどの回答を試しましたが、埋め込みドキュメントで実際に機能するものはありません。フィールドを更新したにもかかわらず、埋め込みドキュメントの未入力フィールドのコンテンツも削除しました。

そのために、@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'
        }
      ]
    }
  ]
})

参考までに、それは実際に私の猫の名前です。

EDIT : 変数名のタイプミス。

于 2014-05-14T14:11:41.700 に答える