polymorphic_on可以在多个列上使用吗?

时间:2014-09-25 14:25:29

标签: python sqlalchemy

我有一个表(我不允许更改它的架构),其中包含由三个类表示的行。

ab中的值确定哪个类对应一行。

if row.a == 'X': return X
elif row.b == 'Y': return Y
else: return Z

显然__polymorhpic_on__没有好列。有没有办法实现这个目标?

1 个答案:

答案 0 :(得分:2)

polymorphic_on可以是列或SQL表达式,因此您只需要找出一个将返回标识值的适当表达式。

在这种情况下,表达式可以是返回将标识子类的常量的case语句。 a == 'X'映射到1b == 'Y'2等。请注意,表达式不返回实例,它会生成用于映射最终实例的SQL表达式。

case(((a == 'X', 1), (b == 'Y', 2)), else_=3)

以下是此解决方案的完整可运行示例。案例陈述已被修改为使用literal_column s。它更冗长,但每次都不需要发送4个绑定参数。

import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session

engine = sa.create_engine('sqlite://', echo=True)
session = Session(bind=engine)
Base = declarative_base(bind=engine)


class ThreeWay(Base):
    __tablename__ = 'three_way'

    id = sa.Column(sa.Integer, primary_key=True)
    a = sa.Column(sa.String)
    b = sa.Column(sa.String)

    __mapper_args__ = {
        'polymorphic_on': sa.case(
            (
                (a == sa.literal_column("'X'"), sa.literal_column('1')),
                (b == sa.literal_column("'Y'"), sa.literal_column('2'))
            ),
            else_=sa.literal_column('3')
        )
    }


class X(ThreeWay):
    __mapper_args__ = {
        'polymorphic_identity': 1
    }

    def __init__(self, **kwargs):
        kwargs['a'] = 'X'
        super(X, self).__init__(**kwargs)


class Y(ThreeWay):
    __mapper_args__ = {
        'polymorphic_identity': 2
    }

    def __init__(self, **kwargs):
        kwargs['b'] = 'Y'
        super(Y, self).__init__(**kwargs)


class Z(ThreeWay):
    __mapper_args__ = {
        'polymorphic_identity': 3
    }


Base.metadata.create_all()

session.add_all((X(), Y(), Z()))
session.commit()

print(session.query(ThreeWay).count())  # inserted 3 generic ThreeWay rows
print(session.query(Y).count())  # 1 of them is specifically a Y row