我有一个表(我不允许更改它的架构),其中包含由三个类表示的行。
列a
和b
中的值确定哪个类对应一行。
if row.a == 'X': return X
elif row.b == 'Y': return Y
else: return Z
显然__polymorhpic_on__
没有好列。有没有办法实现这个目标?
答案 0 :(得分:2)
polymorphic_on
可以是列或SQL表达式,因此您只需要找出一个将返回标识值的适当表达式。
在这种情况下,表达式可以是返回将标识子类的常量的case语句。 a == 'X'
映射到1
,b == '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