#python #mongodb #flask
#python #mongodb #flask
Вопрос:
Я думаю, что мне здесь чего-то не хватает. Я тестирую фреймворки Python Flask и Flask-MongoAlchemy и хочу преобразовать объект в вывод JSON. Вот мой код (абстрагированный):
from flask import Flask
from flaskext.mongoalchemy import MongoAlchemy
try:
from bson.objectid import ObjectId
except:
pass
#a bunch of code to open the mongoDB
class ClassA(db.Document):
title = db.StringField()
field1 = db.StringField()
field2 = db.BoolField()
@app.route('/api/classA', methods=['GET'])
def api_list_all
a = ClassA.query.all()
result = []
for b in a:
result.append(b.wrap())
print result
return json.dumps(result)
Без строки json.dumps оператор print выдает правильный результат. Но только если я запускаю json.dumps по результату, это дает:
Ошибка типа: ObjectId(‘…’) не является сериализуемым в формате JSON
Чего мне не хватает?
Ответ №1:
Результатом является какой-то документ mongo, содержащий содержимое типа ObjectId, которое вам нужно будет указать json, как десериализовать. У вас будет та же проблема с другими типами, специфичными для mongo, такими как ReferenceField(), EmbeddedDocumentField() и т. Д. Вам нужно написать функцию десериализации, которую вы можете передать в json. Что я использую, так это:
def encode_model(obj, recursive=False):
if obj is None:
return obj
if isinstance(obj, (mongoengine.Document, mongoengine.EmbeddedDocument)):
out = dict(obj._data)
for k,v in out.items():
if isinstance(v, ObjectId):
if k is None:
out['_id'] = str(v)
del(out[k])
else:
# Unlikely that we'll hit this since ObjectId is always NULL key
out[k] = str(v)
else:
out[k] = encode_model(v)
elif isinstance(obj, mongoengine.queryset.QuerySet):
out = encode_model(list(obj))
elif isinstance(obj, ModuleType):
out = None
elif isinstance(obj, groupby):
out = [ (g,list(l)) for g,l in obj ]
elif isinstance(obj, (list)):
out = [encode_model(item) for item in obj]
elif isinstance(obj, (dict)):
out = dict([(k,encode_model(v)) for (k,v) in obj.items()])
elif isinstance(obj, datetime.datetime):
out = str(obj)
elif isinstance(obj, ObjectId):
out = {'ObjectId':str(obj)}
elif isinstance(obj, (str, unicode)):
out = obj
elif isinstance(obj, float):
out = str(obj)
else:
raise TypeError, "Could not JSON-encode type '%s': %s" % (type(obj), str(obj))
return out
Затем вы обработаете результат как:
return json.dumps(result, default=encode_model)
или что-то в этом роде.
Комментарии:
1. Имейте в виду, что со временем вам, возможно, придется обрабатывать другие поля. Я продолжаю находить вещи, которые мне нужно сохранить как str(), но не хочу делать это в общем виде (т. Е. Не хочу удалять ошибку raise TypeError).
2. что означает «mongoengine» в этой функции?
3. yourfriendzak —
import mongoengine
Типы (Document, EmbeddedDocument и т. Д.) Определяются в обработчике mongodb, который в данном случае является mongoengine. С таким же успехом это может быть pymongo, или mongoalchemy, или что-то еще.
Ответ №2:
Вы также можете использовать query.raw_output()
метод, чтобы этот экземпляр запроса возвращал необработанные словари Python вместо объекта Python. Со словарем становится легко кодировать в JSON с помощью json.dumps()
:
import json
q=db.query(MyObject)
q.raw_output()
json.dumps(q.first())
Ссылка http://www.mongoalchemy.org/api/expressions/query.html#mongoalchemy .запрос.Запрос.raw_output
Ответ №3:
Объединив два предыдущих ответа, вы должны быть в состоянии сделать что-то вроде этого:
from bson import json_util
# ...
@app.route('/api/classA', methods=['GET'])
def api_list_all
a = ClassA.query.all()
result = []
for b in a:
result.append(b.wrap())
print result
return json_utils.dumps(result) # Change here.