MongoDB ReferenceField в JSON

Я пишу API с Flask. Я использую ReferenceField для пользователя, который написал сообщение. API должен возвращать объект JSON с данными о публикации и данными о пользователе.

Конечная точка колбы:

@app.route("/api/posts")
def posts_index():
    posts = Post.objects()
    return jsonify({
        "posts": json.loads(posts.to_json())
    })

Модель базы данных:

class User(Document):
    email = EmailField(required=True, unique=True)
    username = StringField(max_length=50, required=True, unique=True)
    password = StringField(required=True)
    created = DateTimeField(required=True, default=datetime.datetime.now())


class Comment(EmbeddedDocument):
    content = StringField(max_length=5000)
    user = ReferenceField(User)
    created = DateTimeField(required=True, default=datetime.datetime.now())


class Post(Document):
    title = StringField(max_length=120, required=True)
    user = ReferenceField(User, reverse_delete_rule=CASCADE)
    content = StringField(max_length=5000)
    comments = ListField(EmbeddedDocumentField(Comment))
    created = DateTimeField(required=True, default=datetime.datetime.now())

JSON-ответ:

{
  "posts": [
    {
      "_id": {
        "$oid": "5a5efd1f9ef3161fc64cb95a"
      }, 
      "comments": [], 
      "content": "Lorem Ipsum", 
      "created": {
        "$date": 1516178223018
      }, 
      "title": "Hello", 
      "user": {
        "$oid": "5a5d0b9b9ef316220b6d9881"
      }
    }, 
    {
      "_id": {
        "$oid": "5a5efd1f9ef3161fc64cb95b"
      }, 
      "comments": [], 
      "content": "Lorem Ipsum Dolor", 
      "created": {
        "$date": 1516178223018
      }, 
      "title": "Hello Again", 
      "user": {
        "$oid": "5a5d0b9b9ef316220b6d9881"
      }
    }
  ]
}

Я хотел бы получить ответ json, но ReferenceField должен содержать информацию о пользователе.


person Developer    schedule 17.01.2018    source источник
comment
Справочное поле MongoEngine не хранит документ, вместо этого оно принимает только идентификатор документа, отсюда и название Ссылка. Вы можете сделать это двумя способами: либо использовать User как встроенный документ, либо взять пользовательскую ссылку на каждый Post object из запроса cursor из Post и обновить окончательный ответ, используя цикл.   -  person Sreenadh T C    schedule 19.01.2018
comment
Возможный дубликат Как получить данные ReferenceField в mongoengine?   -  person Sreenadh T C    schedule 19.01.2018


Ответы (2)


Во-первых, вы должны рекурсивно пройтись по всем полям и попытаться получить их значение. Я написал небольшую функцию, которая делает это.

from mongoengine import Document, EmbeddedDocument
from mongoengine.queryset import QuerySet


def to_dict(obj):
    if isinstance(obj, (QuerySet, list)):
        return map(to_dict, obj)
    elif isinstance(obj, (Document, EmbeddedDocument)):
        doc = {}
        for field_name, field_type in obj._fields.iteritems():
            field_value = getattr(obj, field_name)
            doc[field_name] = to_dict(field_value)
        return doc
    else:
        return obj

Вызовите эту функцию как to_dict(Posts.objects) или to_dict(Post.objects.first()) и вы должны получить глубокий словарь.

Затем создайте ответ json с помощью пользовательского кодировщика json.

import json, bson


def json_response(obj, cls=None):
    response = make_response(json.dumps(obj, cls=cls))
    response.content_type = 'application/json'

    return response

class MongoJsonEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, bson.ObjectId):
            return str(obj)

        if isinstance(obj, bson.DBRef):
            return str(obj.id)

        return json.JSONEncoder.default(self, obj)

@app.route('/')
def index():
    posts_json = to_dict(Post.objects)
    return json_response(dict(data=posts_json), cls=MongoJsonEncoder)
person Alk    schedule 26.01.2018
comment
obj._fields.iteritems()изменено на obj._fields.items() при использовании Python 3. Вам также нужно написать return list(map(to_dict, obj)) вместо этого, если return map(to_dict, obj). - person Kruspe; 15.01.2021

hasattr не работает для примера имени атрибута $oid:

value = {'$oid': '5e8764ed70f190b802bdc008'}
if hasattr(value, '$oid'):
    print('this never prints')

Мне пришлось получить к нему доступ следующим образом:

try:
    oid = value['$oid']
    print('this works')
except:
    print('no oid')
person Brian Chojnowski    schedule 03.04.2020
comment
value (плохое имя) — это словарь, поэтому для получения ключа вы используете get, а не hasattr. - person Patrick Mevzek; 03.04.2020