如何使用SQLAlchemy基于ID将对象合并到会话中,同时保持子类的正确鉴别器?

时间:2016-02-15 16:14:49

标签: python sqlalchemy

我在线程之间传递对象ID,并希望能够通过ID将对象合并到会话中。我当然可以跨线程查询对象来实现这一点,但如果对象已经在会话中,则宁愿避免数据库跳转的开销。

我的代码正在运行,但是当我通过父类合并时,它没有正确设置鉴别器值。如何确保正确设置鉴别器?

from sqlalchemy import create_engine, __version__
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column
from sqlalchemy.types import Integer, String

Session = scoped_session(sessionmaker())
Base = declarative_base()

class Person(Base):
    __tablename__ = 'person'
    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    discriminator = Column(String, nullable=False)

    __mapper_args__ = {'polymorphic_identity': 'person',
                       'polymorphic_on': discriminator}

class Programmer(Person):
    __mapper_args__ = {'polymorphic_identity': 'programmer'}

    fav_language = Column(String)

engine = create_engine('postgresql+zxjdbc://mnaber:test123@localhost:5432/ajtest2', echo=True)
Session.configure(bind=engine)
Base.metadata.bind = engine


Base.metadata.drop_all(checkfirst=True)
Base.metadata.create_all()

s = Session()
michael = Programmer(name='Michael', fav_language='Python')
s.add(michael)
s.commit()


print "Sqlalchemy Version: %s" % __version__
print "INITIAL: %s %s" % (type(michael), michael.discriminator)

michael_merged = s.merge(Person(id=michael.id)) #Merge by parent class
print "MERGED: %s %s" % (type(michael_merged), michael_merged.discriminator)
print "FINAL: %s %s" % (type(michael), michael.discriminator)

michael_merged.fav_language = 'Jython'
s.add(michael_merged)
s.commit()

有趣的输出是:

Sqlalchemy Version: 0.8.7
INITIAL: <class '__main__.Programmer'> programmer
MERGED: <class '__main__.Programmer'> person
FINAL: <class '__main__.Programmer'> person

site-packages/sqlalchemy/orm/persistence.py:154: SAWarning: Flushing object <Programmer at 0x18> with incompatible polymorphic identity 'person'; the object may not refresh and/or load correctly
  mapper._validate_polymorphic_identity(mapper, state, dict_)

我应该如何确保MERGED和FINAL有程序员的鉴别器?

1 个答案:

答案 0 :(得分:0)

merge()假设一个对象看起来像你想要的样子,所以你需要在这里传递一个程序员。传入Person(id = 1)当数据库中确实存在该标识的程序员不是受支持的模式时,行为是未定义的。目前发生的事情是你的Person对象在前面设置了“person”的“鉴别器”,因此这是刷新到数据库中的值;它会覆盖会话中已有的内容。

你可以欺骗它像这样工作:

p1 = Person(id=michael.id)
del p1.discriminator
michael_merged = s.merge(p1) #Merge by parent class

但是,我无法保证上述代码将始终适用于未来的SQLAlchemy版本。例如,它不适用于在冲洗时选择“鉴别器”的早期版本。

您的代码示例使得Programmer已经存在于Session中;你可以像这样以类不可知的方式得到这个对象:

obj = session.query(Person).get(michael.id)

然后你有了你的Programmer对象,你可以自由地修改它。