将SqlAlchemy ORM结果转换为dict


问题内容

如何将SQLAlchemy orm对象结果转换为JSON格式?

当前,我正在使用sqlalchemy反射来反映数据库中的表。考虑我有一个用户表和一个地址表,我正在数据库中反映。用户实体与地址实体具有一对一的关系。下面的代码反映数据库中的表并使用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)

现在,当我要将用户对象转换为字典时,请使用以下方法

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。

谁能建议我如何自动确定返回的用户对象是否具有关系,以及如果该关系对象具有任意数量的子对象,则将该关系对象扩展为字典。


问题答案:

您可以使用映射器的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查询。