[英]Convert SqlAlchemy orm result to dict
如何将 SQLAlchemy orm object 结果转换为 JSON 格式?
目前我正在使用 sqlalchemy 反射来反映数据库中的表。 假设我有一个 User 表和一个 Address 表,我正在从数据库中反映出来。 用户实体与地址实体具有一对一的关系。 下面是从数据库反映表并使用映射器 class 到 map 关系的代码。
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 查询 object 时
user = session.query(User).first()
user_dict = object_to_dict(user)
现在,当我想将用户 object 转换为字典时,我使用以下方法
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 与另一个表没有关系,则 object_to_dict 方法可以正常工作并返回有效的 dic object。 如果用户 object 有关系,则 object_to_dict 方法不会自动扩展关系 object 并将其转换为字典。
谁能建议我如何自动确定返回的用户 object 是否有关系,并将关系 object 扩展为一个字典,如果它有一个等等对于任意数量的子对象。
您可以使用映射器的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查询。
尽管“doog adibies”的答案已经被接受并且我对它提出了支持,因为它非常有用,但算法中存在一些值得注意的问题:
found
”) backref
(如果你有一个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
基于“doog abides”和“daveoncode”的回答,以及文档和小的更正(如“iuridiniz”所述)
https://gist.github.com/hrishikeshrt/abb610743c394ce140196498b9c4ff0b
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.