如何将SQLAlchemy orm对象结果转换为JSON格式?
目前我正在使用sqlalchemy反射来反映数据库中的表。 考虑我有一个用户表和一个我正在反映数据库的地址表。 用户实体与地址实体具有一对一的关系。 下面是从DB反映表格的代码,并使用mapper类来映射关系。
from sqlalchemy import Table
from sqlalchemy.orm import mapper, relationship
user_reflection = Table('user', metadata, autoload=True, autoload_with=engine)
class User(object):
def __init__(self, id, name, dob):
self.id = id
self.name = name
self.dob = dob
address_reflection = Table('address', metadata, autoload=True, autoload_with=engine)
mapper(User,
user_reflection,
properties={
'address': relationship(SourceAddress, uselist=False)
}
)
现在,当我使用sqlalchemy orm
查询对象时user = session.query(User).first()
user_dict = object_to_dict(user)
现在,当我想将用户对象转换为dict时 我使用以下方法
def object_to_dict(obj):
columns = [column.key for column in class_mapper(obj.__class__).columns]
get_key_value = lambda c: (c, getattr(obj, c).isoformat()) if isinstance(getattr(obj, c), datetime) else (c, getattr(obj, c))
return dict(map(get_key_value, columns))
但是,object_to_dict方法工作正常,如果返回的用户对象与另一个表没有关系,则返回有效的dic对象。 如果用户对象有关系,则object_to_dict方法不会自动展开关系对象并将其转换为dict。
有人可以建议我如何自动确定返回的用户对象是否有关系,并将关系对象扩展为dict,如果它有一个,依此类推任意数量的子对象。
答案 0 :(得分:10)
您可以使用映射器的relationships属性。代码选择取决于您希望如何映射数据以及关系的外观。如果您有很多递归关系,则可能需要使用max_depth计数器。我的示例使用一组关系来防止递归循环。你可以完全消除递归,如果你只打算深入一个,但你确实说“等等”。
def object_to_dict(obj, found=None):
if found is None:
found = set()
mapper = class_mapper(obj.__class__)
columns = [column.key for column in mapper.columns]
get_key_value = lambda c: (c, getattr(obj, c).isoformat()) if isinstance(getattr(obj, c), datetime) else (c, getattr(obj, c))
out = dict(map(get_key_value, columns))
for name, relation in mapper.relationships.items():
if relation not in found:
found.add(relation)
related_obj = getattr(obj, name)
if related_obj is not None:
if relation.uselist:
out[name] = [object_to_dict(child, found) for child in related_obj]
else:
out[name] = object_to_dict(related_obj, found)
return out
另外,请注意需要考虑的性能问题。您可能希望使用诸如joinedload或subqueryload之类的选项,以防止执行过多的SQL查询。
答案 1 :(得分:3)
尽管" doog adibies"答案已被接受,我对它提出了支持,因为它非常有用,算法中有几个值得注意的问题:
found
")Father
对象与Son
的关系配置为backref
,则会产生额外的关系其中每个子的Father
节点,与主Father
对象已提供的数据相同!)为了解决这些问题,我定义了另一个set()
来跟踪不需要的后退关系,之后我在代码中移动了对访问过的孩子的跟踪。我还故意重命名变量,以便更清楚(当然是IMO)它们代表什么以及算法如何工作,并用更清晰的字典理解取代map()
。
以下是我的实际工作实现,已针对4维嵌套对象(User - > UserProject - > UserProjectEntity - > UserProjectEntityField)进行测试:
def model_to_dict(obj, visited_children=None, back_relationships=None):
if visited_children is None:
visited_children = set()
if back_relationships is None:
back_relationships = set()
serialized_data = {c.key: getattr(obj, c.key) for c in obj.__table__.columns}
relationships = class_mapper(obj.__class__).relationships
visitable_relationships = [(name, rel) for name, rel in relationships.items() if name not in back_relationships]
for name, relation in visitable_relationships:
if relation.backref:
back_relationships.add(relation.backref)
relationship_children = getattr(obj, name)
if relationship_children is not None:
if relation.uselist:
children = []
for child in [c for c in relationship_children if c not in visited_children]:
visited_children.add(child)
children.append(model_to_dict(child, visited_children, back_relationships))
serialized_data[name] = children
else:
serialized_data[name] = model_to_dict(relationship_children, visited_children, back_relationships)
return serialized_data