[英]Using a custom JSON encoder for SQLAlchemy's PostgreSQL JSONB implementation
I am using SQLAlchemy's core library to access some PostgreSQL database. 我正在使用SQLAlchemy的核心库来访问一些PostgreSQL数据库。 Consider I have the following table: 考虑我有下表:
create table foo (j jsonb);
And the following python code: 以下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)
This last sentence fails with the following error: 最后一句失败,出现以下错误:
StatementError("(builtins.TypeError) Decimal('2') is not JSON serializable",)
Which is why I am asking this question: Is there a way to specify a custom encoder for SQLAchemy to use when encoding json data into PostgreSQL dialect? 这就是我提出这个问题的原因:有没有办法为SQLAchemy指定一个自定义编码器,以便在将json数据编码成PostgreSQL方言时使用?
This is supported via the json_serializer
keyword argument to create_engine
, as documented under sqlalchemy.dialects.postgresql.JSON
: 这是通过支持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)
If you, like me, are finding a nice way to get this running with Flask-SQLAlchemy, this is what I did. 如果你像我一样,找到了一个很好的方法来运行Flask-SQLAlchemy,这就是我所做的。 If you import and pass flask.json
instead of the standard library json
module, you'll get automatic deserialization of dates, datetimes and uuid.UUID
instances. 如果导入并传递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)
If you're using Flask, you already have an extended JSONEncoder defined in flask.json
which handles UUID
, but not Decimal
. 如果您使用的瓶,你已经在限定的加长JSONEncoder flask.json
它处理UUID
,而不是Decimal
。 It can be mapped into the SqlAlchemy engine with the json_serializer
param as in @univerio's answer: 它可以使用json_serializer
参数映射到SqlAlchemy引擎,如@ univerio的答案:
from flask import json
engine = create_engine(
app.config['SQLALCHEMY_DATABASE_URI'],
convert_unicode=True,
json_serializer=json.dumps,
)
You can further extend the Flask JSONEncoder
to support decimal.Decimal
with the following: 您可以使用以下内容进一步扩展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
I found anwser here: https://github.com/flask-restful/flask-restful/issues/116#issuecomment-128419699 Summing it up, to run it with Flask-SQLAlchemy: 我在这里找到了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)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.