范围会话上的事件侦听器

时间:2014-01-24 00:25:47

标签: python sqlalchemy flask flask-sqlalchemy event-listener

我正在尝试将事件侦听器添加到Flask应用程序内的SQLAlchemy Session的before_commit事件中。在执行以下操作时

def before_commit(session):
    for item in session:
        if hasattr(item, 'on_save'):
            item.on_save(session)

event.listen(db.session, 'before_commit', before_commit)

我得到了

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "app.py", line 60, in <module>
    event.listen(db.session, 'before_commit', before_commit)
  File "C:\Python27\lib\site-packages\sqlalchemy\event\api.py", line 49, in listen
    _event_key(target, identifier, fn).listen(*args, **kw)
  File "C:\Python27\lib\site-packages\sqlalchemy\event\api.py", line 22, in _event_key
    tgt = evt_cls._accept_with(target)
  File "C:\Python27\lib\site-packages\sqlalchemy\orm\events.py", line 1142, in _accept_with
    "Session event listen on a scoped_session "
sqlalchemy.exc.ArgumentError: Session event listen on a scoped_session requires that its creation callable is associated with the Session class.

我找不到注册事件监听器的正确方法。该文档实际上指出event.listen()也接受scoped_session,但它似乎没有?!


http://docs.sqlalchemy.org/en/latest/orm/events.html#sqlalchemy.orm.events.SessionEvents

  

listen()函数将接受Session对象以及返回结果   sessionmaker()和scoped_session()。

     

此外,它接受Session类,该类将监听器应用于所有Session   全球实例。

2 个答案:

答案 0 :(得分:4)

这意味着您传递给scoped_session()的工厂必须是sessionmaker():

from sqlalchemy.orm import scoped_session, sessionmaker, sessionmaker
from sqlalchemy import event

# good

ss1 = scoped_session(sessionmaker())

@event.listens_for(ss1, "before_flush")
def evt(*arg, **kw):
    pass

# bad

ss2 = scoped_session(lambda: Session)

@event.listens_for(ss2, "before_flush")
def evt(*arg, **kw):
    pass

答案 1 :(得分:0)

再举一个例子,这个代码库不起作用: https://sourceforge.net/p/turbogears1/code/HEAD/tree/branches/1.5/turbogears/database.py

# bad

def create_session():
    """Create a session that uses the engine from thread-local metadata.

    The session by default does not begin a transaction, and requires that
    flush() be called explicitly in order to persist results to the database.

    """
    if not metadata.is_bound():
        bind_metadata()
    return sqlalchemy.orm.create_session()

session = sqlalchemy.orm.scoped_session(create_session)

相反,它需要类似以下内容:

# good

class SessionMakerAndBind(sqlalchemy.orm.sessionmaker):
    def __call__(self, **kw):
        if not metadata.is_bound():
            bind_metadata()
        return super(SessionMakerAndBind, self).__call__(**kw)

sessionmaker = SessionMakerAndBind(autoflush=False, 
                   autocommit=True, expire_on_commit=False)
session = sqlalchemy.orm.scoped_session(sessionmaker)