0

I am writing an API with Flask. I use a ReferenceField for the user who has written a Post. The API should return a JSON object with the data about the post and the data about the user.

Flask endpoint:

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

Database model:

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 response:

{
  "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"
      }
    }
  ]
}

I'd like to get the json response, but the ReferenceField should contain the information about the user.

Developer
  • 2,113
  • 2
  • 18
  • 26
  • MongoEngine Reference Field doesn't store the document, instead it only takes the id of the document, hence the name Reference. You can do this two ways, either have the `User` as [Embedded Document](http://docs.mongoengine.org/apireference.html#mongoengine.EmbeddedDocument) or take user reference of each `Post object` from query `cursor` of `Post` and update the final response using a loop. – Sreenadh T C Jan 19 '18 at 03:57
  • Possible duplicate of [How to get ReferenceField data in mongoengine?](https://stackoverflow.com/questions/23977951/how-to-get-referencefield-data-in-mongoengine) – Sreenadh T C Jan 19 '18 at 04:02

2 Answers2

0

First, you should recursively iterate through all fields end try to get its value. I wrote a small function which does that.

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

Call this function like to_dict(Posts.objects) or to_dict(Post.objects.first()) and you should receive deep dictionary.

Next, create json response with custom json encoder.

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)
Alk
  • 161
  • 3
  • 1
    `obj._fields.iteritems()`changed to `obj._fields.items()` when using Python 3. You also need to write`return list(map(to_dict, obj))` instead if `return map(to_dict, obj)`. – Kruspe Jan 15 '21 at 03:37
0

hasattr is not working for the attribute name $oid example:

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

I had to access it this way:

try:
    oid = value['$oid']
    print('this works')
except:
    print('no oid')