繁体   English   中英

将 SqlAlchemy orm 结果转换为字典

[英]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”的答案已经被接受并且我对它提出了支持,因为它非常有用,但算法中存在一些值得注意的问题:

  1. 关系的子序列化在第一个孩子停止(因为过早添加到“ found ”)
  2. 它还序列化后退关系,在大多数情况下这些关系是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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM