【问题标题】:What is the proper way to model this type of inheritance with SQLAlchemy?用 SQLAlchemy 对这种类型的继承建模的正确方法是什么?
【发布时间】:2013-05-06 02:58:53
【问题描述】:

我有一对 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 表上的“created”和“modified”列不在people 之间共享;这两列对于每个表都是不同的。这些不是绝对必要的,它们只是默认添加到数据库中的每个表定义中,并用于跟踪更改以进行审计/日志记录。

【问题讨论】:

    标签: python orm sqlalchemy


    【解决方案1】:

    您在这里所拥有的不是任何一种类型,因为您复制了一些列而不是其他列。

    来自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. 删除engineer 表中的createdmodified 列。

    【讨论】:

    • 哇。感谢您的详细回复。我已经编辑了问题以解决第 1 点和第 2 点。明天早上我会花更多时间在你的例子上。
    猜你喜欢
    • 2016-11-21
    • 1970-01-01
    • 2015-07-16
    • 2015-02-05
    • 2010-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多