まず、コード例:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import event
Base = declarative_base()
class Foo(Base):
__tablename__ = 'foo'
id = Column(Integer, primary_key=True)
label = Column(String)
class Bar(Base):
__tablename__ = 'bar'
id = Column(Integer, primary_key=True)
foo_id = Column(Integer, ForeignKey('foo.id'))
foo = relationship(Foo)
def __init__(self, foo=None):
self.foo = foo if foo else Foo()
print 'init', self.id, self, self.foo.id, self.foo
def _adjust_label(target, value, old_value, initiator):
print 'adjust', target, value, old_value, initiator
if value and not target.foo.label:
target.foo.label = 'autostring %d' % value
print 'adjust', target.id, target, target.foo.id, target.foo, target.foo.label
event.listen(Bar.id, 'set', _adjust_label)
engine = create_engine('sqlite:////path/to/some.db')
Base.metadata.create_all(engine)
session = sessionmaker(bind=engine)()
bar = Bar()
print 'pre-add', bar.id, bar, bar.foo.id, bar.foo
session.add(bar)
print 'added', bar.id, bar, bar.foo.id, bar.foo
session.commit()
print 'commited', bar.id, bar, bar.foo.id, bar.foo, bar.foo.label
私が得るものは次のとおりです。
pre-add None <__main__.Bar object at 0x2929f50>
pre-add None <__main__.Foo object at 0x292e310>
added None <__main__.Bar object at 0x2929f50>
added None <__main__.Foo object at 0x292e310>
adjust <__main__.Bar object at 0x2929f50> 14 None Bar.id
adjust None <__main__.Bar object at 0x2929f50>
adjust 14 <__main__.Foo object at 0x292e310> autostring 14
commited 14 <__main__.Bar object at 0x2929f50>
commited 14 <__main__.Foo object at 0x2932e10> None
私が驚いたのは、 inがコミット前committed
とbar.foo
は異なるインスタンスであり、その結果、bar.foo.label
in イベント リスナーへの変更が成功したことは窓の外に放り出されたことです。bar.foo.label
の自動生成された一意の文字列が必要であり、純粋なランダム文字列よりも少し意味のあるものが欲しかったので、これをやろうとしています。イベントリスナーの有無にかかわらず、これを自動的に行うことは可能ですか?それとも、ORM モデルよりも高いレベルでこれを処理する必要がありますか?