【问题标题】:SQLAlchemy InheritanceSQLAlchemy 继承
【发布时间】:2010-11-23 03:39:52
【问题描述】:

我对 sqlalchemy 下的继承有点困惑,以至于我什至不确定我应该在这里使用哪种类型的继承(单表、连接表、具体)。我有一个基类,其中包含一些在子类之间共享的信息,以及一些完全独立的数据。有时,我需要来自所有类的数据,有时只需要来自子类的数据。这是一个例子:

class Building:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class Commercial(Building):
    def __init__(self, x, y, business):
        Building.__init__(self, x, y)
        self.business = business

class Residential(Building):
    def __init__(self, x, y, numResidents):
        Building.__init__(self, x, y, layer)
        self.numResidents = numResidents

如何使用声明式将其转换为 SQLAlchemy?那么,如何查询x>5y>3 内的建筑物?或者哪些住宅楼只有一名居民?

【问题讨论】:

    标签: python inheritance sqlalchemy


    【解决方案1】:

    选择如何表示继承主要是数据库设计问题。对于性能来说,单表继承通常是最好的。从良好的数据库设计的角度来看,连接表继承更好。联接表继承使您能够拥有由数据库强制执行的子类的外键,为子类字段设置非空约束要简单得多。具体的表继承是两全其美的。

    声明式的单表继承设置如下所示:

    class Building(Base):
        __tablename__ = 'building'
        id = Column(Integer, primary_key=True)
        building_type = Column(String(32), nullable=False)
        x = Column(Float, nullable=False)
        y = Column(Float, nullable=False)
        __mapper_args__ = {'polymorphic_on': building_type}
    
    class Commercial(Building):
        __mapper_args__ = {'polymorphic_identity': 'commercial'}
        business = Column(String(50))
    
    class Residential(Building):
        __mapper_args__ = {'polymorphic_identity': 'residential'}
        num_residents = Column(Integer)
    

    要使其连接表继承,您需要添加

    __tablename__ = 'commercial'
    id = Column(None, ForeignKey('building.id'), primary_key=True)
    

    到子类。

    两种方法的查询基本相同:

    # buildings that are within x>5 and y>3
    session.query(Building).filter((Building.x > 5) & (Building.y > 3))
    # Residential buildings that have only 1 resident
    session.query(Residential).filter(Residential.num_residents == 1)
    

    要控制加载哪些字段,您可以使用query.with_polymorphic() 方法。

    考虑使用继承进行数据映射的最重要的事情是您是否真的需要继承或可以使用聚合。如果您需要更改建筑物的类型,或者您的建筑物可以同时具有商业和住宅方面,那么继承将是一件痛苦的事情。在这些情况下,通常最好将商业和住宅方面作为相关对象。

    【讨论】:

    • 哇,这是一个很好的答案。谢谢!因此,我比较了单表选项和联接表选项之间的性能,发现第二个查询 [filter(Residential.num_residents == n).count()] 在单表方案中运行速度快了约 2 倍(如预期的那样)。但是,由于某种原因,针对 Building [filter((Building.x > x) & (Building.y > y)).count()] 的第一个查询在单个表中慢了大约 10%,尽管实际上加载了所有元素相当可比 (.all())。
    • 有关连接表继承的更具体问题,请参阅stackoverflow.com/questions/8389606/…
    • 唯一缺少的是一个使用具体表继承的例子——我正在寻求帮助的例子,自然:-) 由于这是一个老问题,也许在这个问题之后添加了具体表继承回答。
    • 这可能是一个愚蠢的问题,但底层 SQL 表在单表继承中的外观如何?是否只有 1 个包含 businessnum_residents 列的 building SQL 表?
    • @LymanZerga:是的,这就是它在数据库中的样子。它在单个表上创建所有列,并根据polymorphic_on 引用的字段的值在 Python 中简单地创建正确的模型。
    【解决方案2】:

    Ants Aasma 的解决方案要优雅得多,但如果您有意将类定义与表定义分开,则需要使用 mapper 函数将类映射到表。定义类后,您需要定义表:

    building = Table('building', metadata,
        Column('id', Integer, primary_key=True),
        Column('x', Integer),
        Column('y', Integer),
    )
    commercial = Table('commercial', metadata,
        Column('building_id', Integer, ForeignKey('building.id'), primary_key=True),
        Column('business', String(50)),
    )
    residential = Table('residential', metadata,
        Column('building_id', Integer, ForeignKey('building.id'), primary_key=True),
        Column('numResidents', Integer),
    )

    然后您可以将表映射到类:

    mapper(Building, building)
    mapper(Commercial, commercial, inherits=Building, polymorphic_identity='commercial')
    mapper(Residential, residential, inherits=Building, polymorphic_identity='residential')

    然后以与 Ants Aasma 描述的完全相同的方式与类交互。

    【讨论】:

    • 您能具体说明mapper 的来源吗?是sqlalchemy.orm.mapper吗?
    • 这太棒了!我正在寻找类似的东西来在 Python 中实现 Clean Architecture(由 Bob 叔叔编写)任何指向阅读有关此方法和映射器函数的更多信息都会非常好!
    • 如果有人想了解更多关于这种将对象映射到类的技术,它被称为经典映射,它不同于通常使用的声明式映射。要了解更多信息,请查看以下链接:docs.sqlalchemy.org/en/13/orm/mapping_styles.html
    • 有一本漂亮的(在我看来)书,它实现了基于经典映射的存储库模式:cosmicpython.com/book/chapter_02_repository.html
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-08
    • 1970-01-01
    • 2012-10-06
    • 2015-03-04
    • 2017-12-16
    • 1970-01-01
    • 2012-10-19
    相关资源
    最近更新 更多