2016-04-05 4 views
6

Я использую основную библиотеку SQLAlchemy для доступа к некоторой базе данных PostgreSQL. Рассмотрим У меня есть следующая таблица:Использование пользовательского кодировщика JSON для реализации PostgreSQL JSONB для SQLAlchemy

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?

ответ

11

Это поддерживается с помощью аргумента 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) 
+0

Высокими. Пропущена эта часть документации. Попробуем завтра и примите ответ. Благодаря!! –

3

Если вы, как я, найти хороший способ, чтобы получить это работает с Колба-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) 
1

Если вы используете флягу, у вас уже есть расширенный JSONEncoder определены в flask.json, который обрабатывает UUID, но не Decimal. Он может быть отображен в двигатель SQLAlchemy с json_serializer парами, как в ответе @ univerio в:

from flask import json 

engine = create_engine(
    app.config['SQLALCHEMY_DATABASE_URI'], 
    convert_unicode=True, 
    json_serializer=json.dumps, 
) 

Вы можете в дальнейшем расширить Настой 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 
Смежные вопросы