2016-04-05 14 views
6

Sto utilizzando la libreria principale di SQLAlchemy per accedere ad alcuni database PostgreSQL. Considerate Ho la seguente tabella:Utilizzo di un encoder JSON personalizzato per l'implementazione JSONB PostgreSQL di SQLAlchemy

create table foo (j jsonb); 

E il seguente codice 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) 

Quest'ultima frase non riesce con il seguente errore:

StatementError("(builtins.TypeError) Decimal('2') is not JSON serializable",) 

è per questo che chiedo questo domanda: esiste un modo per specificare un codificatore personalizzato per SQLAchemy da utilizzare durante la codifica dei dati JSON nel dialetto PostgreSQL?

risposta

11

Questo è supportato tramite l'argomento json_serializer parola chiave per create_engine, come documentato in 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

impressionante. Mancava quella parte della documentazione. Lo proverò domani e accetterò la risposta di conseguenza. Grazie!! –

3

Se, come me, stanno trovando un bel modo per ottenere questo correre con la boccetta-SQLAlchemy, questo è ciò che L'ho fatto. Se importi e passi flask.json invece del modulo della libreria standard json, riceverai la deserializzazione automatica di date, datetimes e istanze 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

Se stai usando Flask, è già un JSONEncoder esteso definito in flask.json che gestisce UUID, ma non Decimal. Si può essere mappato nel motore SQLAlchemy con la json_serializer param come nella risposta di @ univerio:

from flask import json 

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

si può ulteriormente estendere il Flask JSONEncoder per sostenere decimal.Decimal con il seguente:

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 
Problemi correlati