SQLAlchemy 中的具体表继承模式详解
什么是具体表继承
具体表继承(Concrete Table Inheritance)是 SQLAlchemy 提供的一种继承映射策略,也称为"每个类一个表"(table-per-class)模式。在这种模式下,继承体系中的每个类都对应数据库中的一个独立表,每个表都包含该类的所有属性(包括继承的属性)。
具体表继承的特点
- 独立表结构:每个子类都有自己完整的表结构,包含所有字段
- 无公共基表:与联合表继承不同,基类Person也有对应的表
- 多态查询:可以通过基类查询所有子类实例
- 明确标识:每个表通过
polymorphic_identity
标识具体类型
代码解析
基础设置
intpk = Annotated[int, mapped_column(primary_key=True)]
str50 = Annotated[str, mapped_column(String(50))]
这里定义了两个类型注解,用于简化列定义:
intpk
:整数主键str50
:长度50的字符串
基类定义
class Person(ConcreteBase, Base):
__tablename__ = "person"
id: Mapped[intpk]
company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
name: Mapped[str50]
company: Mapped[Company] = relationship(back_populates="employees")
__mapper_args__ = {
"polymorphic_identity": "person",
}
关键点:
- 继承自
ConcreteBase
和Base
,ConcreteBase
是具体继承必需的 - 定义了所有子类共有的字段:id、company_id和name
- 设置了多态标识
polymorphic_identity
子类定义
class Engineer(Person):
__tablename__ = "engineer"
id: Mapped[int] = mapped_column(primary_key=True)
company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
name: Mapped[str50]
status: Mapped[str50]
engineer_name: Mapped[str50]
primary_language: Mapped[str50]
__mapper_args__ = {"polymorphic_identity": "engineer", "concrete": True}
关键点:
- 必须重新定义所有字段,包括继承的字段
__mapper_args__
中必须设置concrete=True
- 有自己的
polymorphic_identity
多态查询
eng_manager = with_polymorphic(Person, "*")
session.scalars(
select(eng_manager).filter(
or_(
eng_manager.Engineer.engineer_name == "engineer1",
eng_manager.Manager.manager_name == "manager2",
)
)
).all()
with_polymorphic
允许我们同时查询基类和子类的属性,"*"
表示加载所有子类。
具体表继承的优缺点
优点
- 查询性能好,不需要多表连接
- 表结构清晰,每个类对应一个完整表
- 适合子类有大量特有属性的情况
缺点
- 需要重复定义继承的字段
- 修改基类字段需要同步修改所有子类
- 多态查询可能较复杂
适用场景
具体表继承适合以下情况:
- 继承层次较浅
- 子类有大量特有属性
- 多态查询不是主要操作
- 需要避免表连接带来的性能问题
总结
SQLAlchemy的具体表继承提供了一种高效的继承映射方式,特别适合子类差异较大的场景。通过本文的示例和解析,开发者可以更好地理解和使用这一特性,在自己的项目中实现合适的继承策略。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考