jsonify在Flask中的SQLAlchemy结果集

mal*_*wan 112 python sqlalchemy flask flask-sqlalchemy

我正在尝试在Flask/Python中jsonify一个SQLAlchemy结果集.

Flask邮件列表建议采用以下方法:http://librelist.com/browser//flask/2011/2/16/jsonify-sqlalchemy-pagination-collection-result/#04a0754b63387f87e59dda564bde426e:

return jsonify(json_list = qryresult)
Run Code Online (Sandbox Code Playgroud)

但是我收到以下错误:

TypeError: <flaskext.sqlalchemy.BaseQuery object at 0x102c2df90> 
is not JSON serializable
Run Code Online (Sandbox Code Playgroud)

我在这里俯瞰什么?

我发现了这个问题:如何将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
Run Code Online (Sandbox Code Playgroud)

pla*_*aes 155

看来你实际上还没有执行你的查询.试试以下:

return jsonify(json_list = qryresult.all())
Run Code Online (Sandbox Code Playgroud)

[编辑]:jsonify问题是,通常对象不能自动进行jsonified.甚至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]
Run Code Online (Sandbox Code Playgroud)

现在我可以做的观点:

return jsonify(json_list=[i.serialize for i in qryresult.all()])
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助 ;)

  • @Mohamed 7 年前,这是有道理的。我创建的 `serialize` 是一个属性,因为我不需要向它推送任何参数..当然,`as_json` 会是更好的名字。 (2认同)
  • 我真的不处理它。这个答案只是一个快速解决方案...... (2认同)

bit*_*cle 36

我有同样的需要,序列化为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__)
Run Code Online (Sandbox Code Playgroud)

  • 您还可以扩展声明性Base类,以自动在所有模型中包含`json`属性. (2认同)

Car*_*rot 34

这通常对我来说足够了:

我创建了一个序列化mixin,我和我的模型一起使用.序列化函数基本上获取SQLAlchemy检查器公开的任何属性并将其放入dict中.

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]
Run Code Online (Sandbox Code Playgroud)

现在所需要的只是使用Serializermixin类扩展SQLAlchemy模型.

如果有您不希望公开的字段,或者需要特殊格式化的字段,只需覆盖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
Run Code Online (Sandbox Code Playgroud)

在您的控制器中,您所要做的就是在结果上调用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))
Run Code Online (Sandbox Code Playgroud)

  • 如果您对get_users使用jsonify,则语法必须为:return jsonify(users = User.serialize_list(users)) (4认同)

n0n*_*ker 18

这是我的方法:https: //github.com/n0nSmoker/SQLAlchemy-serializer

pip安装SQLAlchemy-serializer

您可以轻松地将mixin添加到模型中,而不仅仅是在它的实例上调用.to_dict()方法

您也可以在SerializerMixin的基础上编写自己的mixin

  • 有趣的解决方案.我不得不添加elif isinstance(value,str):在elif hasattr之前的ret = value(value,'__ iter_'):在python3中避免无限递归 (2认同)

reu*_*ano 5

对于平面查询(无联接),您可以执行此操作

@app.route('/results/')
def results():
    data = Table.query.all()
    result = [d.__dict__ for d in data]
    return jsonify(result=result)
Run Code Online (Sandbox Code Playgroud)

如果您只想从数据库中返回某些列,则可以执行此操作

@app.route('/results/')
def results():
    cols = ['id', 'url', 'shipping']
    data = Table.query.all()
    result = [{col: getattr(d, col) for col in cols} for d in data]
    return jsonify(result=result)
Run Code Online (Sandbox Code Playgroud)

  • TypeError:InstanceState 类型的对象不是 JSON 可序列化不是解决方案 (2认同)

rob*_*bru 5

好的,我已经研究了几个小时,并且已经开发出了我认为是迄今为止最pythonic的解决方案。以下代码段是python3,但如果需要的话,应该不要太费劲地向后移植。

我们要做的第一件事是从一个mixin开始,它使您的数据库模型有点像dicts:

from sqlalchemy.inspection import inspect

class ModelMixin:
    """Provide dict-like interface to db.Model subclasses."""

    def __getitem__(self, key):
        """Expose object attributes like dict values."""
        return getattr(self, key)

    def keys(self):
        """Identify what db columns we have."""
        return inspect(self).attrs.keys()
Run Code Online (Sandbox Code Playgroud)

现在,我们将继承mixin来定义我们的模型:

class MyModel(db.Model, ModelMixin):
    id = db.Column(db.Integer, primary_key=True)
    foo = db.Column(...)
    bar = db.Column(...)
    # etc ...
Run Code Online (Sandbox Code Playgroud)

这一切都需要能够通过一个实例MyModel()dict(),并得到一个活生生的dict例子了吧,这会让我们致力于使相当长的路jsonify()了解它。接下来,我们需要扩展JSONEncoder以获取其余的方法:

from flask.json import JSONEncoder
from contextlib import suppress

class MyJSONEncoder(JSONEncoder):
    def default(self, obj):
        # Optional: convert datetime objects to ISO format
        with suppress(AttributeError):
            return obj.isoformat()
        return dict(obj)

app.json_encoder = MyJSONEncoder
Run Code Online (Sandbox Code Playgroud)

优点:如果模型包含计算字段(也就是说,您希望JSON输出包含实际上未存储在数据库中的字段),那么这也很容易。只需将您的计算字段定义为@propertys,然后keys()像这样扩展方法:

class MyModel(db.Model, ModelMixin):
    id = db.Column(db.Integer, primary_key=True)
    foo = db.Column(...)
    bar = db.Column(...)

    @property
    def computed_field(self):
        return 'this value did not come from the db'

    def keys(self):
        return super().keys() + ['computed_field']
Run Code Online (Sandbox Code Playgroud)

现在对jsonify进行琐碎的工作:

@app.route('/whatever', methods=['GET'])
def whatever():
    return jsonify(dict(results=MyModel.query.all()))
Run Code Online (Sandbox Code Playgroud)


Adv*_*sus 5

如果您正在使用,flask-restful可以使用marshal

from flask.ext.restful import Resource, fields, marshal

topic_fields = {
    'title':   fields.String,
    'content': fields.String,
    'uri':     fields.Url('topic'),
    'creator': fields.String,
    'created': fields.DateTime(dt_format='rfc822')
}

class TopicListApi(Resource):
    def get(self):
        return {'topics': [marshal(topic, topic_fields) for topic in DbTopic.query.all()]}
Run Code Online (Sandbox Code Playgroud)

您需要明确列出您要返回的内容以及它的类型,对于 api 来说我更喜欢这样。序列化很容易处理(不需要jsonify),日期也不是问题。请注意,该字段的内容uri是根据topic端点和 ID 自动生成的。