【问题标题】:Relationship to find ancestor in SQLAlchemy在 SQLAlchemy 中查找祖先的关系
【发布时间】:2017-11-14 13:46:43
【问题描述】:

我有一棵看起来像这样的一对多关系树:

组织->运营->区域->区域->站点

organization 是顶级祖先。 site 有一个area_idarea 有一个region_id,依此类推。

对于组织下的每个实体,我希望能够通过site.organizationarea.organization 等关系快速访问组织祖先。

每个实体上的relationship 会是什么样子才能完成这项工作?

【问题讨论】:

    标签: python orm sqlalchemy


    【解决方案1】:

    解决方案是为那些与组织没有直接关系的类使用composite "secondary" join。例如网站:

    class Site(Base):
    
        organization = relationship(
            "Organization",
            secondary="join(Operation, Region).join(Area)",
            uselist=False,
            innerjoin=True,
            viewonly=True)
    

    而区域应该只使用表操作作为辅助:

    class Region(Base):
    
        organization = relationship(
            "Organization",
            secondary="operation",
            uselist=False,
            innerjoin=True,
            viewonly=True)
    

    以上是仅显示关系配置的简化版本。一个具体的例子如下:

    from sqlalchemy import *
    from sqlalchemy.orm import *
    from sqlalchemy.ext.declarative import declarative_base
    
    engine = create_engine("sqlite://")
    
    Base = declarative_base()
    Base.metadata.bind = engine
    
    Session = sessionmaker()
    
    class Organization(Base):
        __tablename__ = 'organization'
        id = Column(Integer, primary_key=True)
    
    class Operation(Base):
        __tablename__ = 'operation'
        id = Column(Integer, primary_key=True)
        organization_id = Column(ForeignKey('organization.id'))
        organization = relationship("Organization")
    
    class Region(Base):
        __tablename__ = 'region'
        id = Column(Integer, primary_key=True)
        operation_id = Column(ForeignKey('operation.id'))
        organization = relationship(
            "Organization",
            secondary="operation",
            uselist=False, innerjoin=True, viewonly=True)
        operation = relationship("Operation")
    
    class Area(Base):
        __tablename__ = 'area'
        id = Column(Integer, primary_key=True)
        region_id = Column(ForeignKey('region.id'))
        organization = relationship(
            "Organization",
            secondary="join(Operation, Region)",
            uselist=False, innerjoin=True, viewonly=True)
        region = relationship("Region")
    
    class Site(Base):
        __tablename__ = 'site'
        id = Column(Integer, primary_key=True)
        area_id = Column(ForeignKey('area.id'))
        organization = relationship(
            "Organization",
            secondary="join(Operation, Region).join(Area)",
            uselist=False, innerjoin=True, viewonly=True)
        area = relationship("Area")
    
    Base.metadata.create_all()
    
    session = Session()
    session.add(Site(area=Area(region=Region(operation=Operation(organization=Organization())))))
    session.commit()
    
    site = session.query(Site).options(joinedload(Site.organization)).first()
    print(site)
    print(site.organization)
    print(site.area.organization)
    print(site.area.region.organization)
    print(site.area.region.operation.organization)
    

    【讨论】:

    • 这似乎不起作用,并且链接的文档令人困惑......显示的示例在每个子实体上都有一个顶级实体的外键。就我而言,每个实体中的直接父级只有一个外键。
    • 我能说什么,它对我有用。如果您在问题本身中提供实际的示例代码,那么生成开箱即用的答案会更容易。如果没有您的代码,此答案中的示例必然需要进行一些微调。大体思路是:创建一个派生表/复合表作为辅助,主要是需要的连接。由于您所追求的是多对一,但需要辅助,因此您必须通过 uselist=False 才能产生标量关系。如果您的外键不可为空,请同时传递 innerjoin=True
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多