'Add sqlalchemy relationship dinamically by relationship name (str) or object
I'm parsing deeply nested XML hierarchical files and persisting data to PostgreSQL with SQLAlchemy. Parsing each tree in file is quite simple: get element attributes, rename it according to naming map, create SQLA Object, parse children elements, add appropriate relationships (parent children), repeat.
To avoid boilerplate code I'm trying to implement recursive solution. So far I've managed to implement creation of list of SQLA objects but now struggling adding relationships dinamically based on pre-determined helper dict (e.g. I will explicitly state which relationship to create)
Very shortened example
<ApplicationSessionStart TradeId="085747" EventTime="2021-05-02T09:00:00">
<LotList>
<LotInfo LotNumber="1"/>
</LotList>
</ApplicationSessionStart>
SQLA Classes with relationship:
class ApplicationSessionStart(Base):
__tablename__ = "application_session_start"
id = Column(Integer, primary_key=True, autoincrement="auto")
trade_id_etp = Column(String(100), nullable=False)
event_time = Column(DateTime, nullable=False)
# Relationships
lot_info = relationship(
"LotInfo",
back_populates="application_session_start",
uselist=True,
)
class LotInfo(Base):
__tablename__ = "lot_info"
id = Column(Integer, primary_key=True, autoincrement="auto")
lot_number = Column(String(5), nullable=True)
# FKs
application_session_start_id = Column(
Integer,
ForeignKey("application_session_start.id"),
)
# Relationships
application_session_start = relationship(
"ApplicationSessionStart",
back_populates="lot_info",
uselist=True,
)
Helper dict to describe XML data logic & structure
gdm = {
"ApplicationSessionStart": {
"model": ApplicationSessionStart,
"children": {LotInfo: "LotInfo.application_session_start"},
"naming": {
"EventTime": "event_time",
"TradeId": "trade_id",
},
},
"LotInfo": {
"model": LotInfo,
"children": None,
"naming": {"LotNumber": "lot_number"},
},
}
Function to recursively create objects
def rec_create_class_from_elem(elem, gdm, result_list=None):
elem_name = elem.tag
expected_children = gdm.get(elem_name).get("children")
if result_list is None:
result_list = []
elem_attrs_dict = elem.attrib
elem_correct_dict = map_naming_v2(elem_name, gdm, elem_attrs_dict)
model = gdm.get(elem_name).get("model")
obj = model(**elem_correct_dict)
result_list.append(obj)
if expected_children is not None:
for k, v in expected_children.items():
model_name_str = k.__name__
for child in elem.findall(".//" + model_name_str):
rec_create_class_from_elem(child, gdm, result_list)
return result_list
[<models.ApplicationSessionStart object at 0x7eff2f6be6d0>, <models.LotInfo object at 0x7eff2f6bec10>]
Solution 1:[1]
I've found out that this is possible by calling setattr
setattr(obj, relationship, [parent_obj])
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Krank |
