在Flask中将SQLAlchemy结果集转换为JSON
我正在尝试在Flask/Python中将SQLAlchemy的结果集转换成JSON格式。
Flask的邮件列表建议了以下方法:http://librelist.com/browser//flask/2011/2/16/jsonify-sqlalchemy-pagination-collection-result/#04a0754b63387f87e59dda564bde426e。
return jsonify(json_list = qryresult)
但是我遇到了以下错误:
TypeError: <flaskext.sqlalchemy.BaseQuery object at 0x102c2df90>
is not JSON serializable
我这里忽略了什么呢?
我发现了这个问题:如何将SqlAlchemy的结果序列化为JSON?,看起来很相似,但我不知道Flask是否有一些特别的功能可以让这个过程更简单,就像邮件列表帖子提到的那样。
编辑:为了更清楚,这就是我的模型的样子:
class Rating(db.Model):
__tablename__ = 'rating'
id = db.Column(db.Integer, primary_key=True)
fullurl = db.Column(db.String())
url = db.Column(db.String())
comments = db.Column(db.Text)
overall = db.Column(db.Integer)
shipping = db.Column(db.Integer)
cost = db.Column(db.Integer)
honesty = db.Column(db.Integer)
communication = db.Column(db.Integer)
name = db.Column(db.String())
ipaddr = db.Column(db.String())
date = db.Column(db.String())
def __init__(self, fullurl, url, comments, overall, shipping, cost, honesty, communication, name, ipaddr, date):
self.fullurl = fullurl
self.url = url
self.comments = comments
self.overall = overall
self.shipping = shipping
self.cost = cost
self.honesty = honesty
self.communication = communication
self.name = name
self.ipaddr = ipaddr
self.date = date
15 个回答
我也遇到过类似的需求,需要把数据转成json格式。你可以看看这个问题。里面讲了怎么通过编程的方式来发现表的列。基于这个,我写了下面的代码。对我来说,这个代码很好用,我会在我的网页应用中使用它。
def to_json(inst, cls):
"""
Jsonify the sql alchemy query result.
"""
convert = dict()
# add your coversions for things like datetime's
# and what-not that aren't serializable.
d = dict()
for c in cls.__table__.columns:
v = getattr(inst, c.name)
if c.type in convert.keys() and v is not None:
try:
d[c.name] = convert[c.type](v)
except:
d[c.name] = "Error: Failed to covert using ", str(convert[c.type])
elif v is None:
d[c.name] = str()
else:
d[c.name] = v
return json.dumps(d)
class Person(base):
__tablename__ = 'person'
id = Column(Integer, Sequence('person_id_seq'), primary_key=True)
first_name = Column(Text)
last_name = Column(Text)
email = Column(Text)
@property
def json(self):
return to_json(self, self.__class__)
以下是我通常用的方法:
我会创建一个序列化的混合类,然后把它用在我的模型上。这个序列化的功能基本上是获取 SQLAlchemy 检查器能看到的所有属性,然后把它们放到一个字典里。
from sqlalchemy.inspection import inspect
class Serializer(object):
def serialize(self):
return {c: getattr(self, c) for c in inspect(self).attrs.keys()}
@staticmethod
def serialize_list(l):
return [m.serialize() for m in l]
现在只需要把 SQLAlchemy 模型扩展一下,加入 Serializer
这个混合类就可以了。
如果有些字段你不想显示,或者需要特别的格式,只需在模型的子类中重写 serialize()
这个函数就行。
class User(db.Model, Serializer):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String)
password = db.Column(db.String)
# ...
def serialize(self):
d = Serializer.serialize(self)
del d['password']
return d
在你的控制器里,你只需要对结果调用 serialize()
函数(如果查询结果是一个列表,就用 serialize_list(l)
):
def get_user(id):
user = User.query.get(id)
return json.dumps(user.serialize())
def get_users():
users = User.query.all()
return json.dumps(User.serialize_list(users))
看起来你实际上并没有执行你的查询。试试下面的方法:
return jsonify(json_list = qryresult.all())
[编辑]: 使用 jsonify 的问题在于,通常这些对象不能自动转换成 JSON 格式。就连 Python 的日期时间类型也会失败 ;)
我过去做过的一个方法是,给需要转换的类添加一个额外的属性(比如 serialize
)。
def dump_datetime(value):
"""Deserialize datetime object into string form for JSON processing."""
if value is None:
return None
return [value.strftime("%Y-%m-%d"), value.strftime("%H:%M:%S")]
class Foo(db.Model):
# ... SQLAlchemy defs here..
def __init__(self, ...):
# self.foo = ...
pass
@property
def serialize(self):
"""Return object data in easily serializable format"""
return {
'id' : self.id,
'modified_at': dump_datetime(self.modified_at),
# This is an example how to deal with Many2Many relations
'many2many' : self.serialize_many2many
}
@property
def serialize_many2many(self):
"""
Return object's relations in easily serializable format.
NB! Calls many2many's serialize property.
"""
return [ item.serialize for item in self.many2many]
现在在视图中,我可以直接这样做:
return jsonify(json_list=[i.serialize for i in qryresult.all()])
[编辑 2019]: 如果你有更复杂的对象或者循环引用,可以使用像 marshmallow 这样的库。