Как получить данные ReferenceField в mongoengine?

У меня проблема с поиском набора запросов oid в JSON, и я хотел бы получить фактическое имя пользователя из этой коллекции пользователей, у меня есть ниже:

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()

И вот результат, который я получаю:

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

Было бы неплохо иметь что-то вроде этого:

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

или как-то похоже, как "from_user" : {"User": {"username":"holms"}}

ОБНОВИТЬ

Вышеупомянутый ответ работает для ReferenceField, который находится в Document, но как насчет ссылок EmbeddedDocument? Допустим, я добавлю "сообщения" только ()?

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

В настоящее время я нашел способ составления словаря, что довольно болезненно.

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'}]

ОБНОВЛЕНИЕ 2 Кажется, это было сделано трюк

 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)

и вы получите это:)

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

    }

1 ответ

Решение

В настоящее время это не поддерживается напрямую, так как MongoEngine поддерживает только расширенный синтаксис json mongodb.

Все, что делает mongoengine под прикрытием, это использование pymongo's json_utils сбросить данные. Нет никаких причин, почему вы не можете использовать это явно, например:

    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": []}

обновленный

Пример с настраиваемым набором запросов:

   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": []}]'
Другие вопросы по тегам