[英]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.