使用SQLAlchemy为这种类型的继承建模的正确方法是什么?

时间:2013-05-06 02:58:54

标签: python orm sqlalchemy

我有一对看起来像这样的PostgreSQL表(简化示例):

CREATE TABLE people (
    id SERIAL PRIMARY KEY,
    created timestamp with time zone DEFAULT now() NOT NULL,
    modified timestamp with time zone NULL,
    email varchar NOT NULL UNIQUE,
    inactive boolean NOT NULL DEFAULT False
);

CREATE TABLE engineers (
    id integer NOT NULL REFERENCES people(id) ON DELETE CASCADE ON UPDATE CASCADE UNIQUE,
    created timestamp with time zone DEFAULT now() NOT NULL,
    modified timestamp with time zone NULL,
    login_name varchar NOT NULL UNIQUE,
    PRIMARY KEY(id)
);

engineers继承自people,因为“id”列people作为外键传递给engineers。此列也定义为两个表中的主键。如果我想使用某人的电子邮件地址查询login_name,可以通过SQL:

这样做
SELECT p.email FROM engineers e 
  JOIN people p ON p.id = e.id 
  WHERE p.inactive = False AND e.login_name = 'john.smith';

如何使用SQLAlchemy的声明式样式建模此关系并执行类似的查询?似乎“Joined Table Inheritance”描述了我的使用场景,但我的表中没有鉴别器列。我也一直试图使用“Concrete Table Inheritance”,但我只是设法迷惑自己。

我很感激任何建议。谢谢!

被修改

这种表结构的原因是因为“人”可能是“工程师”,“会计师”或“测试者”(或三者的某种组合)。 people表包含每个人共有的属性,例如姓名,电话,号码,雇用日期等。

engineers表格中的“已创建”和“已修改”列未在people之间共享;两列与每个表不同。这些并非绝对必要,它们只是默认添加到数据库中的每个表定义,并用于跟踪审计/日志记录的更改。

1 个答案:

答案 0 :(得分:0)

你所拥有的内容并不清楚,因为你复制了一些列而不是其他列。

来自docs

  

SQLAlchemy支持三种继承形式:单表继承,其中几种类型由单个表表示,具体表继承,其中每种类型的类都是由独立表和连接表继承表示,其中类层次结构在依赖表之间分解,每个类由其自己的表表示,该表仅包括该类本地的那些属性。

因为您复制了createdmodified列,所以这是具体继承的候选者。但是,因为您希望Employee是一个不完整的实体而没有来自Person的列,所以这也是表继承的连接。

我建议你尝试使其适合连接表继承,但我不完全确定映射器如何处理重复列的存在。在连接表继承中,可以将子类分别作为关系进行操作,但我不确定它们将如何合并,是否可以合并,或者可以使用哪种映射器配置来处理使用哪一个。如果有办法,this section可能会告诉你如何。

然而,这是让你入门的东西。 polymorphic_on can also be any sql expression (scroll down to the polymorphic_on argument) - 它不必是一个列。如果您的唯一子类是engineers表,则在鉴别器中使用EXISTS子查询。

下面是一些未经测试的代码,如果不稍微搞乱它可能无法正常工作 - 它只是向您展示了您必须使用的模式。

people_table = Table('people', metadata,
    Column('id', Integer, primary_key=True),
    Column('email', String, unique=True),
    Column('inactive', Boolean, default=False),
)

engineers_table = Table('engineers', metadata,
    Column('id', Integer, ForeignKey('people.id'), primary_key=True),
    Column('engineer_info', String),
)

class Person(object):
    pass

class Engineer(Person):
    pass


discriminator = case(
    [
        (exists().where(people_table.c.id==engineers_table.c.id), 'engineer'),
    ], else_='person')

mapper(Person, people_table, polymorphic_identity='person', polymorphic_on=discriminator)
mapper(Engineer, engineer_table, polymorphic_identity='engineer')

毋庸置疑,如果可能的话,你应该为自己的理智而努力:

  1. 在您的person表格中添加一个鉴别器列。
  2. 摆脱created表格中的modifiedengineer列。