2016-04-05 11 views
6

Używam biblioteki rdzeniowej SQLAlchemy do uzyskania dostępu do niektórych baz danych PostgreSQL. Rozważmy mam poniższej tabeli:Używanie niestandardowego enkodera JSON do implementacji SQLAlchemy PostgreSQL JSONB

create table foo (j jsonb); 

i następujący kod Pythona:

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) 

To ostatnie zdanie nie powiedzie się z powodu następującego błędu:

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

dlatego pytam to pytanie: czy istnieje sposób na określenie niestandardowego kodera dla SQLAchemy do użycia podczas kodowania danych json w dialekcie PostgreSQL?

Odpowiedz

11

ten jest wspierany poprzez argument json_serializer kluczowego do create_engine, co zostało udokumentowane w ramach 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

niesamowite. Pominięto tę część dokumentacji. Wypróbuję to jutro i odpowiednio zaakceptuję odpowiedź. Dzięki!! –

3

Jeśli, tak jak ja, znajdujemy piękny sposób, aby uzyskać to działa z kolbą-SQLAlchemy, to co Zrobiłem. Jeśli importujesz i przekazujesz flask.json zamiast standardowego modułu biblioteki json, otrzymasz automatyczną deserializację dat, dat i organizacji oraz uuid.UUID wystąpień.

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

Jeśli używasz kolbie masz już rozszerzony JSONEncoder zdefiniowane w flask.json który obsługuje UUID, ale nie Decimal. To może być odwzorowany do silnika sqlalchemy z json_serializer param jak w odpowiedzi @ univerio za:

from flask import json 

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

można dodatkowo rozszerzyć kolby JSONEncoder wspierać decimal.Decimal z następujących czynności:

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 
Powiązane problemy