我正在使用SQLAlchemy的核心库访问一些PostgreSQL数据库。考虑我有下表:
create table foo (j jsonb);
以下python代码:
from decimal import *
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey, DateTime
from sqlalchemy.dialects.postgresql import JSONB
metadata = MetaData(schema="public")
foo = Table('foo', metadata,Column('f', JSONB))
d = Decimal(2)
ins = foo.insert().values(j = {'d': d})
# assuming engine is a valid sqlalchemy's connection
engine.execute(ins)
最后一句失败,出现以下错误:
StatementError("(builtins.TypeError) Decimal('2') is not JSON serializable",)
这就是我提出这个问题的原因:有没有办法为SQLAchemy指定一个自定义编码器,以便在将json数据编码成PostgreSQL方言时使用?
答案 0 :(得分:20)
json_serializer
下的create_engine
关键字参数支持此功能,如sqlalchemy.dialects.postgresql.JSON
所述:
def _default(val):
if isinstance(val, Decimal):
return str(val)
raise TypeError()
def dumps(d):
return json.dumps(d, default=_default)
engine = create_engine(..., json_serializer=dumps)
答案 1 :(得分:4)
如果你像我一样,正在寻找一种很好的方法来运行Flask-SQLAlchemy,这就是我所做的。如果导入并传递flask.json
而不是标准库json
模块,则会自动反序列化日期,日期时间和uuid.UUID
个实例。
class HackSQLAlchemy(SQLAlchemy):
""" Ugly way to get SQLAlchemy engine to pass the Flask JSON serializer
to `create_engine`.
See https://github.com/mitsuhiko/flask-sqlalchemy/pull/67/files
"""
def apply_driver_hacks(self, app, info, options):
options.update(json_serializer=json.dumps)
super(HackSQLAlchemy, self).apply_driver_hacks(app, info, options)
答案 2 :(得分:2)
如果你正在使用Flask,你已经在flask.json
中定义了一个扩展的JSONEncoder,它处理UUID
,但不是Decimal
。它可以使用json_serializer
参数映射到SqlAlchemy引擎,如@ univerio的答案所示:
from flask import json
engine = create_engine(
app.config['SQLALCHEMY_DATABASE_URI'],
convert_unicode=True,
json_serializer=json.dumps,
)
您可以使用以下内容进一步扩展Flask JSONEncoder
以支持decimal.Decimal
:
import decimal
from flask import json
class CustomJSONEncoder(json.JSONEncoder):
"""
Override Flask's JSONEncoder with the single method `default`, which
is called when the encoder doesn't know how to encode a specific type.
"""
def default(self, obj):
if type(obj) is decimal.Decimal:
return str(obj)
else:
# raises TypeError: obj not JSON serializable
return json.JSONEncoder.default(self, obj)
def init_json(app):
"""
Use custom JSON encoder with Flask
"""
app.json_encoder = CustomJSONEncoder
答案 3 :(得分:0)
我在这里找到了anwser:https://github.com/flask-restful/flask-restful/issues/116#issuecomment-128419699对其进行总结,以与Flask-SQLAlchemy一起运行:
from flask import Flask, json
from decimal import Decimal
# define encoder
class JSONEncoder(json.JSONEncoder):
def default(self, value):
if isinstance(value, Decimal):
return str(value)
return json.JSONEncoder.default(self, value)
class Config:
RESTFUL_JSON = {}
# make sure RESTful and Flask encoders stay synchronized
@staticmethod
def init_app(app):
app.config['RESTFUL_JSON']['cls'] = app.json_encoder = JSONEncoder
app = Flask(__name__)
app.config.from_object(Config)
Config.init_app(app)