SQLAlchemy继承

Noa*_*oah 49 python inheritance sqlalchemy

我对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
Run Code Online (Sandbox Code Playgroud)

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

Ant*_*sma 81

选择如何表示继承主要是数据库设计问题.对于性能,单表继承通常是最好的.从良好的数据库设计角度来看,连接表继承更好.连接表继承使您可以拥有数据库强制执行的子类的外键,对子类字段具有非空约束要简单得多.具体的表继承是两个世界中最糟糕的.

具有声明性的单表继承设置如下所示:

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)
Run Code Online (Sandbox Code Playgroud)

要使其加入表继承,您需要添加

__tablename__ = 'commercial'
id = Column(None, ForeignKey('building.id'), primary_key=True)
Run Code Online (Sandbox Code Playgroud)

到子类.

两种方法的查询大致相同:

# 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)
Run Code Online (Sandbox Code Playgroud)

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

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

  • 哇,这是一个很好的答案.谢谢!所以我比较了单表和联接表选项之间的性能,发现第二个查询[filter(Residential.num_residents == n).count()]在单表方案中运行速度快2倍(如预期的那样).但是,出于某种原因,针对Building [filter((Building.x> x)&(Building.y> y)).count()]的第一个查询对于单个表来说慢了大约10%,尽管实际上加载了所有元素非常可比(.all()). (2认同)
  • 唯一缺少的是使用具体表继承的示例 - 我自然正在寻求帮助:-)由于这是一个老问题,也许在回答这个问题后添加了具体表继承。 (2认同)

ada*_*dam 12

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),
)
Run Code Online (Sandbox Code Playgroud)

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

mapper(Building, building)
mapper(Commercial, commercial, inherits=Building, polymorphic_identity='commercial')
mapper(Residential, residential, inherits=Building, polymorphic_identity='residential')
Run Code Online (Sandbox Code Playgroud)

然后与Ants Aasma所描述的完全相同的方式与类进行交互.

  • 您能具体说明“mapper”来自什么吗?是“sqlalchemy.orm.mapper”吗? (3认同)