Плоска реалізація
Ви можете використовувати щось подібне:
from sqlalchemy.ext.declarative import DeclarativeMeta
class AlchemyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj.__class__, DeclarativeMeta):
# an SQLAlchemy class
fields = {}
for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
data = obj.__getattribute__(field)
try:
json.dumps(data) # this will fail on non-encodable values, like other classes
fields[field] = data
except TypeError:
fields[field] = None
# a json-encodable dict
return fields
return json.JSONEncoder.default(self, obj)
а потім конвертувати в JSON за допомогою:
c = YourAlchemyClass()
print json.dumps(c, cls=AlchemyEncoder)
Він буде ігнорувати поля, які не кодуються (встановіть їх у "None").
Він не розширює відносини автоматично (оскільки це може призвести до самонавіювання та вільного циклу).
Рекурсивна некругова реалізація
Якщо, однак, ви віддаєте перевагу циклу назавжди, ви можете використовувати:
from sqlalchemy.ext.declarative import DeclarativeMeta
def new_alchemy_encoder():
_visited_objs = []
class AlchemyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj.__class__, DeclarativeMeta):
# don't re-visit self
if obj in _visited_objs:
return None
_visited_objs.append(obj)
# an SQLAlchemy class
fields = {}
for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
fields[field] = obj.__getattribute__(field)
# a json-encodable dict
return fields
return json.JSONEncoder.default(self, obj)
return AlchemyEncoder
А потім кодуйте об'єкти, використовуючи:
print json.dumps(e, cls=new_alchemy_encoder(), check_circular=False)
Це кодувало б усіх дітей, і всіх їх дітей, і всіх їх дітей ... По суті, можливо, кодуйте всю вашу базу даних. Коли вона досягне щось, що було закодовано раніше, воно кодуватиме це як "Ніхто".
Рекурсивна, можливо, кругова, вибіркова реалізація
Ще одна альтернатива, напевно, краща - це можливість вказати поля, які потрібно розширити:
def new_alchemy_encoder(revisit_self = False, fields_to_expand = []):
_visited_objs = []
class AlchemyEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj.__class__, DeclarativeMeta):
# don't re-visit self
if revisit_self:
if obj in _visited_objs:
return None
_visited_objs.append(obj)
# go through each field in this SQLalchemy class
fields = {}
for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
val = obj.__getattribute__(field)
# is this field another SQLalchemy object, or a list of SQLalchemy objects?
if isinstance(val.__class__, DeclarativeMeta) or (isinstance(val, list) and len(val) > 0 and isinstance(val[0].__class__, DeclarativeMeta)):
# unless we're expanding this field, stop here
if field not in fields_to_expand:
# not expanding this field: set it to None and continue
fields[field] = None
continue
fields[field] = val
# a json-encodable dict
return fields
return json.JSONEncoder.default(self, obj)
return AlchemyEncoder
Тепер ви можете зателефонувати за допомогою:
print json.dumps(e, cls=new_alchemy_encoder(False, ['parents']), check_circular=False)
Наприклад, лише розширити поля SQLAlchemy під назвою "батьки".