如何从sqlalchemy中对象进行jsonify?

时间:2013-11-24 06:10:26

标签: javascript python jquery sqlalchemy

我正在使用Flask,SQLAlchemy和javascript。我需要通过AJAX将我的查询结果传递给json格式的javascript,但我一直收到这个错误:

raise TypeError(repr(o) + " is not JSON serializable")
TypeError: <model.Cloud object at 0x7f72e40c5910> is not JSON serializable'

我的代码如下:

@app.route('/past/<user_id>')
def get_past_clouds(user_id):
    if user_id:
        clouds = model_session.query(model.User).filter_by(id=user_id).first().clouds
        if clouds != "":
            clouds_d = {}
            for idx, cloud in enumerate(clouds):
                clouds_d[idx] = cloud
            return jsonify(json_list=clouds_d)
    return None

忽略我以前的评论,该评论允许请求生效,但返回的所有对象都未定义。

这就是我最终的表现:

clouds_d = dict( (cloud.name, [ photo.to_dict() for photo in cloud.photos ]) for cloud in clouds )
return jsonify(clouds_d)

6 个答案:

答案 0 :(得分:1)

您已经发现,SQLAlchemy模型不能本地化为JSON。

您采用的方法-从模型实例的属性中手动构建字典-完成工作,但是如果您需要对多个模型进行json处理,在序列化中处理值转换或支持反序列化(将JSON转换回SQLAlchemy模型实例),您会发现自己编写了大量繁琐的代码。

更好的解决方案是对SQLAlchemy使用序列化/反序列化扩展库,例如SQLAthanorMarshmallow-SQLAlchemy(完整披露:我是SQLAthanor的作者)。

使用这两个库中的任何一个,您基本上都可以将SQLAlchemy模型序列化和反序列化为其他格式,或者将它们反序列化为SQLAlchemy模型实例。

对于SQLAthanor,基本上可以通过一个方法调用来完成工作:

json_result = model_instance.dump_to_json()

或(具有更多配置,可让您进行更细粒度的控制):

json_result = model_instance.to_json()

您还可以序列化为dict,YAML或CSV。

相反的过程是:

model_instance = ModelClass.new_from_json(json_result)

希望这会有所帮助!

答案 1 :(得分:0)

基于TypeError: <model.Cloud object at 0x7f72e40c5910> is not JSON serializable' 在你的clouds_d词典里面似乎有一个嵌套在里面的Cloud对象。请点赞print clouds_d

答案 2 :(得分:0)

就像错误消息所说的那样,你不能在SQLAlchemy模型对象上调用jsonify(我认为你也不能腌制它们)。尝试:

clouds_d[idx] = dict((k, cloud.__dict__[k]) for k in cloud.__dict__ if k[0] != '_')

或更好,明确:

clouds_d[idx] = {
    'id': cloud.id,
    'other_attrs': '...',
}

答案 3 :(得分:0)

经过一些更多的摆弄,我修复了它,这是如何:

@app.route('/past/<user_id>')
def get_past_clouds(user_id):
    if user_id:
        clouds = model_session.query(model.User).filter_by(id=user_id).first().clouds
        if clouds != "":
            clouds_d = {}
            for idx, cloud in enumerate(clouds):
                clouds_d[idx] = [ photo.path + photo.filename for photo in cloud.photos ]
            print "clouds: ", clouds_d
            return json.dumps(clouds_d)
    return None

看起来SQLAlchemy对象不可序列化,所以我不得不从它返回的对象中得到我想要的东西。

答案 4 :(得分:0)

这是我用python编写的解决方案:

步骤1:自定义JSON.encoder

import json
import uuid
from pprint import pformat
from datetime import datetime
from collections import OrderedDict

class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        # cls = self.__class__
        if isinstance(obj, datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S.%f')
        elif isinstance(obj, uuid.UUID):
            return str(obj)
        elif hasattr(obj, '__html__'):
            return str(obj.__html__())
        elif isinstance(obj, OrderedDict):
            m = json.dumps(obj)
        elif hasattr(obj, 'to_dict'):
            return obj.to_dict()
        else:
            mp = pformat(obj, indent=2)
            print("JsonEncodeError", type(obj), mp)
            m = json.JSONEncoder.default(self, obj)
        return m

步骤2:覆盖flask_app.json_encoder

from flask import Flask
app = Flask(__name__)
app.json_encoder = CustomJSONEncoder

第3步:为Helper Class的sqlalchemy自定义A metaclass

class CoModel():
    """
    A `Helper Class` for `metaclass` of sqlalchemy
    usage 1 : flask_sqlalchemy

        from flask_sqlalchemy import SQLAlchemy
        db = SQLAlchemy(session_options=session_options)
        class TableName(db.Model, CoModel):
            pass


    usage 2: sqlalchemy

        from sqlalchemy.ext.declarative import declarative_base
        Base = declarative_base()
        class TableName(Base, CoModel):
            __tablename__ = 'table_name'
    """

    def to_dict(self):
        result = {}
        columns = self.columns()
        for col in columns:
            name = col.name
            value = getattr(self, name)
            result[name] = value
        return result

    @classmethod
    def columns(cls):
        tbl = getattr(cls, "__table__", None)
        if tbl is None:
            raise NameError('use sample: class TableName(db.Model, CoModel)')
        else:
            return tbl.columns

最后,如果您回复flask.jsonify,它将由CoModel.to_dict()自动编码

当然,您也可以使用json.dumps(sqlalchmey_object,cls=CustomJSONEncoder)

答案 5 :(得分:0)

使用marshmallow-sqlalchemy模式,我正在生成一个对象,并将可序列化的JSON对象作为模型的本机方法返回。这是SQLAlchemy模型中的Customer类的示例。

from models import db
import datetime
from marshmallow_sqlalchemy import ModelSchema

class Customer(db.Model):
    __tablename__ = 'customers'
    __table_args__ = {'schema': 'abc_schema'}
    id = db.Column(db.String(255), primary_key=True)
    created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow)
    firstName = db.Column(db.String())
    lastName = db.Column(db.String())
    def __getitem__(self, item):
        return getattr(self, item)

    ## Making the sql alchemy object JSON serailizable
    def JSONSerializableObj(self):
        class BaseSchema(ModelSchema): 
            def on_bind_field(self, field_name, field_obj):
                field_obj.allow_none = True

        class ObjectSchema(BaseSchema): 
            class Meta:
                model = self

        object_schema = ObjectSchema()
        return object_schema.dump(self).data

这样,我将对所有模型重新使用此代码段,并使用JSONSerializableObj