通过关系名称 (str) 或对象动态添加 sqlalchemy 关系

发布于 2025-01-10 11:44:52 字数 2764 浏览 0 评论 0原文

我正在解析深度嵌套的 XML 分层文件并使用 SQLAlchemy 将数据保存到 PostgreSQL。解析文件中的每个树非常简单:获取元素属性,根据命名映射对其进行重命名,创建 SQLA 对象,解析子元素,添加适当的关系(父子元素),然后重复。

为了避免样板代码,我正在尝试实现递归解决方案。到目前为止,我已经成功地实现了 SQLA 对象列表的创建,但现在努力根据预先确定的辅助字典动态添加关系(例如,我将明确说明要创建哪个关系)

非常简短的示例

<ApplicationSessionStart TradeId="085747" EventTime="2021-05-02T09:00:00">
  <LotList>
      <LotInfo LotNumber="1"/>
   </LotList>
</ApplicationSessionStart>

SQLA 类with 关系:

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,
    )

描述 XML 数据逻辑的辅助字典 结构

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"},
    },
}

递归创建对象的

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
函数

[]

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>]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一生独一 2025-01-17 11:44:53

我发现通过调用 setattr 可以做到这一点

setattr(obj, relationship, [parent_obj])

I've found out that this is possible by calling setattr

setattr(obj, relationship, [parent_obj])
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文