Как с помощью mongoengine + marshmallow описать самовложенные модели?

Есть документ в MongoDB:

{
  "_id": {
    "$oid": "5e7a12dbf89bd64d88f73750"
  },
  "type": "_t",
  "value": "Proj",
  "elements": [
    {
      "type": "_p",
      "value": "Glob",
      "elements": [
        {
          "type": "_v",
          "value": "3qm21lOV13_vWp0uTxo2z8"
        }
      ]
    },
    {
      "type": "_p",
      "value": "Owner",
      "elements": [
        {
          "type": "_t",
          "value": "OH",
          "attributes": {
            "nil": true,
            "ref": {
              "$oid": "5e77c8055e59b043d4acdf10"
            }
          }
        }
      ]
    },
    {
      "type": "_p",
      "value": "Count",
      "elements": [
        {
          "type": "_v",
          "value": 500.0
        }
      ]
    }
  ]
}

Как видно, имеет место самовложение, в списке elements могут быть объекты структурно идентичные корневому. Вопрос, как правильно описать модель для подобного рода структуры и как потом это сериализовать в JSON с помощью marshmallow? Я пробую создать модели и зациклить вложенную на саму себя:

class EmbeddedElement(me.EmbeddedDocument):
    type = me.StringField(required=True, max_length=16)
    value = me.DynamicField()
    attributes = me.DictField()
    elements = me.EmbeddedDocumentListField(document_type="EmbeddedElement")


class Element(me.Document):
    type = me.StringField(required=True, max_length=16)
    value = me.DynamicField()
    attributes = me.DictField()
    elements = me.EmbeddedDocumentListField(document_type=EmbeddedElement)

    meta = {'collection': 'projects'}

Далее описываю схему marshmallow:

class TempSchema(Schema):
    id = fields.String()
    ref = fields.String()

    class Meta:
        model = Element
        fields = ('id', 'type', 'value', 'attributes', 'elements', 'ref', 'nil')

При попытке сделать:

e = Element.objects.skip(offset).limit(per_page)
temp_schema.dump(e)

Вылетает ошибка - TypeError: Object of type EmbeddedElement is not JSON serializable. Как с подобным бороться? Полагаю, что дело не только в невозможности сериализовать EmbeddedElement, правильно ли вообще так описывать самовложенные модели, если нет, то как правильно?


Ответы (1 шт):

Автор решения: Филипп Бондарев

Ответ: Да, такое определение моделей будет работать, хотя в изящности метода я сильно сомневаюсь. Для того, что бы всё корректно сериализовалось, следует добавить схему для вложенного объекта attributes:

class AttributeSchema(Schema):
    ref = fields.String()

    class Meta:
        fields: Tuple[str] = ('nil', 'ref', 'cType')

Таким образом, мы определим как сериализовать ObjectId во вложенных объектах. Так же необходимо в уже существующие схемы добавить списки с вложенными схемами:

class EmbeddedElementSchema(Schema):
    id = fields.String()
    elements = fields.List(fields.Nested('EmbeddedElementSchema'))
    attributes = fields.Nested(AttributeSchema)

    class Meta:
        fields = ('id', 'type', 'value', 'attributes', 'elements')


class ElementSchema(EmbeddedElementSchema):

    class Meta:
        fields = ('id', 'type', 'value', 'attributes', 'elements')

И voilà, можно подавать.

→ Ссылка