after_insert()事件是执行此操作的一种方法,您可能会注意到它传递了一个SQLAlchemy
Connection对象,而不是
Session其他与刷新相关的事件那样的对象。映射程序级别的刷新事件通常用于直接在给定的条件下调用SQL
Connection:
@event.listens_for(Comment, "after_insert")def after_insert(mapper, connection, target): thread_table = Thread.__table__ thread = target.thread connection.execute( thread_table.update(). where(thread_table.c.id==thread.id). values(word_count=sum(c.word_count for c in thread.comments)) ) print "updated cached word count to", thread.word_count
这里值得注意的是,与再次在整个工作单元过程中运行该属性更改相比,直接调用UPDATE语句的性能要高得多。
但是,这里实际上并不需要诸如after_insert()之类的事件,因为我们知道刷新发生之前“
word_count”的值。实际上,我们知道它是因为Comment和Thread对象彼此关联,并且我们还可以使用属性事件始终保持Thread.word_count在内存中完全新鲜:
def _word_count(msg): return len(msg.split())@event.listens_for(Comment.message, "set")def set(target, value, oldvalue, initiator): if target.thread is not None: target.thread.word_count += (_word_count(value) - _word_count(oldvalue))@event.listens_for(Comment.thread, "set")def set(target, value, oldvalue, initiator): # the new Thread, if any if value is not None: value.word_count += _word_count(target.message) # the old Thread, if any if oldvalue is not None: oldvalue.word_count -= _word_count(target.message)
此方法的最大优点是,也不需要遍历thread.comments,对于卸载的集合意味着将发出另一个SELECT。
还有一种方法是在before_flush()中进行操作。以下是一个快速而肮脏的版本,可以对其进行完善以更仔细地分析已更改的内容,以确定是否需要更新word_count:
@event.listens_for(Session, "before_flush")def before_flush(session, flush_context, instances): for obj in session.new | session.dirty: if isinstance(obj, Thread): obj.word_count = sum(c.word_count for c in obj.comments) elif isinstance(obj, Comment): obj.thread.word_count = sum(c.word_count for c in obj.comments)
我会选择属性事件方法,因为它是性能最高且最新的方法。



