SQLAlchemy 继承

73 投票
2 回答
33098 浏览
提问于 2025-04-15 13:53

我对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?或者哪些住宅建筑只有1个居民?

2 个回答

19

Ants Aasma的解决方案看起来更简洁,但如果你故意把类的定义和表的定义分开,那么你需要用映射函数把类和表联系起来。在你定义好类之后,接下来要定义你的表:

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所描述的那样与类进行互动了。

114

选择如何表示继承关系主要是一个数据库设计的问题。为了提高性能,通常使用单表继承是最好的选择。从良好的数据库设计角度来看,连接表继承更为优越。连接表继承可以让数据库强制执行子类的外键,这样在子类字段上设置非空约束会简单很多。而具体表继承则是两者的缺点结合。

单表继承的设置方式看起来是这样的:

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() 方法。

使用继承进行数据映射时,最重要的是考虑你是否真的需要继承,或者是否可以使用聚合。如果你将来需要更改建筑物的类型,或者你的建筑物既有商业用途又有住宅用途,那么使用继承会很麻烦。在这种情况下,通常更好将商业和住宅方面作为相关对象来处理。

撰写回答