我有一个表(我不允许改变它的模式),它包含三个类表示的行。polymorhpic_on可以用于多个列吗?
列a
和b
中的值确定哪一个类对应一行。
if row.a == 'X': return X
elif row.b == 'Y': return Y
else: return Z
显然没有为__polymorhpic_on__
好列。有没有办法做到这一点?
我有一个表(我不允许改变它的模式),它包含三个类表示的行。polymorhpic_on可以用于多个列吗?
列a
和b
中的值确定哪一个类对应一行。
if row.a == 'X': return X
elif row.b == 'Y': return Y
else: return Z
显然没有为__polymorhpic_on__
好列。有没有办法做到这一点?
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
表达式必须包含被映射的实例,但是在表达式将被评估的地方没有可用的'self'。表达式中“a”的含义是什么?它似乎是一个列(而不是列值),它肯定不等于'X'。 – 2014-09-25 15:15:15
好吧,我明白了,它非常好用,非常感谢! – 2014-09-25 15:30:24
如果'row.a =='X'和row.b =='Y''怎么办? X优先于Y吗?我想是的,根据你写的规则,但只是想澄清。 – davidism 2014-09-25 14:34:46
是的,你是对的 – 2014-09-25 14:37:55