在Flask中将SQLAlchemy结果集转换为JSON

141 投票
15 回答
197382 浏览
提问于 2025-04-16 23:51

我正在尝试在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 个回答

38

我也遇到过类似的需求,需要把数据转成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__)
58

以下是我通常用的方法:

我会创建一个序列化的混合类,然后把它用在我的模型上。这个序列化的功能基本上是获取 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))
185

看起来你实际上并没有执行你的查询。试试下面的方法:

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 这样的库。

撰写回答