Python 炼金术遗传

Python 炼金术遗传,python,inheritance,sqlalchemy,Python,Inheritance,Sqlalchemy,我对sqlalchemy下的继承有点困惑,甚至不确定我应该在这里使用什么类型的继承(单表、联接表、具体)。我有一个基类,它包含一些在子类之间共享的信息,以及一些完全独立的数据。有时,我需要所有类的数据,有时只需要子类的数据。下面是一个例子: class Building: def __init__(self, x, y): self.x = x self.y = y class Commercial(Building): def __init__

我对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>5
y>3
范围内的建筑?或者哪些住宅建筑只有一个居民?

选择如何表示遗产主要是一个数据库设计问题。为了提高性能,单表继承通常是最好的。从良好的数据库设计角度来看,联接表继承更好。联接表继承使您能够拥有数据库强制执行的子类的外键,子类字段具有非null约束要简单得多。具体的表继承是两种情况中最糟糕的一种

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

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\u polymorphic()
方法


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

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描述的方式与类交互。

哇,这是一个很好的答案。谢谢因此,我比较了单表和联接表选项之间的性能,发现第二个查询[filter(Residential.num_residents==n).count()]在单表场景中运行速度快了约2倍(如预期的那样)。但是,由于某种原因,对Building[filter((Building.x>x)和(Building.y>y)).count()的第一次查询在单个表中大约慢10%,尽管实际上加载所有元素的速度相当(.all())。有关联接表继承的更具体的问题,请参见唯一缺少的是一个使用具体表继承的示例--我正在寻求帮助,当然:-)由于这是一个老问题,可能在回答此问题后添加了具体表继承。这可能是一个愚蠢的问题,但是,在单表继承中,底层SQL表是什么样子的呢?是否只有一个
building
SQL表,其中包括
business
num\u-residents
列?@LymanZerga:是的,这就是它在数据库中的外观。它在单个表上创建所有列,并根据
polymorphic\u on
引用的字段值简单地在Python中创建正确的模型。您能指定
mapper
的来源吗?是不是
sqlalchemy.orm.mapper
?太棒了!我一直在寻找类似这样的东西,以便在Python中实现干净的体系结构(由Bob叔叔编写),任何指向阅读更多有关此方法和映射器函数的指针都将非常好!如果有人想更多地了解这种将对象映射到类的技术,我们称之为经典映射,它不同于通常使用的声明性映射。要了解更多信息,请查看以下链接:在我看来,有一本关于的好书实现了基于经典映射的存储库模式: