How to get ReferenceField data in mongoengine?

15,766

Currently this is not supported directly as MongoEngine only supports mongodb's extended json syntax.

All mongoengine does under the covers is use pymongo's json_utils to dump the data. Theres no reason why you can't use it explicitly eg:

    from bson import json_util

    class Inbox(db.Document):
        from_user    = db.ReferenceField(User, required=True)
        subject      = db.StringField(max_length=255, required=True)
        created_at   = db.DateTimeField(default=datetime.now)
        messages     = db.ListField(db.EmbeddedDocumentField(Message))


        def to_json(self):
            data = self.to_mongo() // get the pymongo representation of the document
            data["from_user"] = {"User": {"username": self.from_user.username}}
            return json_util.dumps(data)

            User.drop_collection()

    ...

    Inbox.drop_collection()

    ross = User(username="Ross").save()
    Inbox(from_user=ross, subject="Mongoengine should make json easier").save()

    doc = Inbox.objects.only('from_user', 'subject', 'created_at').get()
    print doc.to_json()

    {"_id": {"$oid": "538c3d71c3d384172fe35393"}, 
     "from_user": {"User": {"username": "Ross"}}, 
     "subject": "Mongoengine should make json easier", 
     "created_at": {"$date": 1401703297198}, "messages": []}

Updated

Example with custom queryset:

   from bson import json_util

    class CustomQuerySet(QuerySet):
         def to_json(self):
            return "[%s]" % (",".join([doc.to_json() for doc in self]))

    class Inbox(Document):
        from_user    = ReferenceField(User, required=True)
        subject      = StringField(max_length=255, required=True)
        created_at   = DateTimeField(default=datetime.now)
        messages     = ListField(EmbeddedDocumentField(Message))

        meta = {'queryset_class': CustomQuerySet}

        def to_json(self):
            data = self.to_mongo()
            data["from_user"] = {"User": {"username": self.from_user.username}}
            return json_util.dumps(data)

  ...
  ipdb> Inbox.objects.only('from_user', 'subject', 'created_at').to_json()
        '[{"_id": {"$oid": "538d84cbc3d3843eeeb5dbbe"}, 
           "from_user": {"User": {"username": "Ross"}}, 
           "subject": "Mongoengine should make json easier", 
           "created_at": {"$date": 1401787099246}, "messages": []}]'
Share:
15,766
holms
Author by

holms

Just a DevOps, who's passionate about scaling and automation. Who doesn't want to do less job anyway? DevOps practices brought a new way to decrease maintenance for the whole company, which is basically increase of profit up to 80% in some cases! My background is mostly Python with HA web services. Currently working with Kubernetes and Terraform.

Updated on July 19, 2022

Comments

  • holms
    holms almost 2 years

    I'm have a problem that query set retrieving oid in json, and I would like to retrieve actual username of that User collection I have below:

    class User(db.Document):
        username      = db.StringField(required=True)
        password_hash = db.StringField()
        is_admin      = db.IntField(default=0)
    
    class Message(db.EmbeddedDocument):
        to_users     = db.ListField(db.ReferenceField(User))
        created_at   = db.DateTimeField(default=datetime.now)
        is_read      = db.BooleanField(default=False)
        body         = db.StringField(required=True)
    
    class Inbox(db.Document):
        from_user    = db.ReferenceField(User, required=True)
        subject      = db.StringField(max_length=255, required=True)
        created_at   = db.DateTimeField(default=datetime.now)
        messages     = db.ListField(db.EmbeddedDocumentField(Message))
    
    username = User().get_username()
    username = User.objects(username=username).first()
    
    inbox = Inbox.objects.filter(messages__to_users__in=[username]).only('from_user', 'subject', 'created_at').to_json()
    

    And that's the result which I get:

    [{"created_at": {"$date": 1401593024844}, "from_user": {"$oid": "538ad45fb43fdd69076d3e64"}, "subject": "test"}]
    

    Would be nice to have something like this:

    [{"created_at": {"$date": 1401593024844}, "from_user": {"username": "holms"}, "subject": "test"}]
    

    or somehow similar like "from_user" : {"User": {"username":"holms"}}

    UPDATE

    Above answer works for the ReferenceField that is in Document, but what about EmbeddedDocument references? Let's say I'll add "messages" to only()?

    In [97]: inbox = Inbox.objects(messages__to_users__in=[username]).only('from_user', 'subject', 'created_at', 'messages').select_related()
    

    Currently I've found the way by mapping dictionary which is quite painful.

    In [125]: for entry in inbox:
        item = dict(subject=entry.subject, messages=map(lambda msg: dict(body=msg.body, is_read=msg.is_read, to_users=map(lambda usr: usr.username, msg.to_users)), entry.messages), from_user=entry.from_user.username)
        result.append(item)
       .....:
    
    In [126]: result
    Out[126]:
    [
     {'from_user': u'holms',
      'messages': [{'body': u'test body',
        'is_read': False,
        'to_users': [u'holms']}],
      'subject': u'test'}]
    

    UPDATE 2 Seems to be this done the trick

     57     def to_json(self):
     58         data = self.to_mongo() # get pymongo representation
     59         data["from_user"] = {"User": {"username": self.from_user.username} }
     60
     61         for key, reply in enumerate(data["messages"]):
     62             for user in self.messages[key].to_users:
     63                 print user.username
     64             reply["to_users"] = {"User": {"username": user.username} }
     65
     66         return json_util.dumps(data)
    

    and you get this :)

        {
           "_id":{
              "$oid":"538ad500b43fdd690e2eefb5"
           },
           "from_user":{
              "User":{
                 "username":"holms"
              }
           },
           "subject":"test",
           "created_at":{
              "$date":1401593024844
           },
           "messages":[
              {
                 "to_users":{
                    "User":{
                       "username":"holms"
                    }
                 },
                 "created_at":{
                    "$date":1401593024845
                 },
                 "is_read":false,
                 "body":"test body"
              }
           ]
    
        }