版本:1.1.0b2 |发布日期:2016年7月1日

SQLAlchemy 1.1文档

使用事件跟踪对象和会话更改

SQLAlchemy具有在整个Core和ORM中使用的广泛的Event Listening系统。在ORM内部,有各种各样的事件监听器钩子,这些钩子被记录在ORM Events的API级别。这一系列事件在过去几年中一直在增长,包括许多非常有用的新事件以及一些与以前不相关的老事件。本节将尝试介绍主要事件挂钩以及何时可以使用它们。

持久性事件

可能是最广泛使用的一系列事件是“持久性”事件,它们对应于flush processflush是所有关于对象的未决修改的决定,然后以INSERT,UPDATE和DELETE的形式发送到数据库。

before_flush()

当应用程序想要确保在刷新过程中对数据库进行额外的持久性更改时,SessionEvents.before_flush()挂钩是迄今为止最常用的事件。使用SessionEvents.before_flush()来操作对象来验证它们的状态,以及在持久化之前编写其他对象和引用。Within this event, it is safe to manipulate the Session’s state, that is, new objects can be attached to it, objects can be deleted, and indivual attributes on objects can be changed freely, and these changes will be pulled into the flush process when the event hook completes.

The typical SessionEvents.before_flush() hook will be tasked with scanning the collections Session.new, Session.dirty and Session.deleted in order to look for objects where something will be happening.

For illustrations of SessionEvents.before_flush(), see examples such as Versioning with a History Table and Versioning using Temporal Rows.

after_flush()

The SessionEvents.after_flush() hook is called after the SQL has been emitted for a flush process, but before the state of the objects that were flushed has been altered. That is, you can still inspect the Session.new, Session.dirty and Session.deleted collections to see what was just flushed, and you can also use history tracking features like the ones provided by AttributeState to see what changes were just persisted. SessionEvents.after_flush()事件中,可以根据观察到的变化将其他SQL发送到数据库。

after_flush_postexec()

SessionEvents.after_flush_postexec() is called soon after SessionEvents.after_flush(), but is invoked after the state of the objects has been modified to account for the flush that just took place. Session.newSession.dirtySession.deleted集合在这里通常是完全空的。使用SessionEvents.after_flush_postexec()检查最终对象的标识映射,并可能发出额外的SQL。In this hook, there is the ability to make new changes on objects, which means the Session will again go into a “dirty” state; the mechanics of the Session here will cause it to flush again if new changes are detected in this hook if the flush were invoked in the context of Session.commit(); otherwise, the pending changes will be bundled as part of the next normal flush. 当钩子检测到Session.commit()中的新变化时,计数器确保在100次迭代之后在这方面的无限循环停止,在SessionEvents.after_flush_postexec()

映射级事件

除了flush级别的钩子之外,还有一些钩子更加细化,因为它们是以每个对象为基础调用的,并且基于INSERT,UPDATE或DELETE分解。这些是映射器持久性钩子,它们也非常流行,但是这些事件需要更谨慎地处理,因为它们已经在正在进行的刷新过程的范围内进行。许多操作在这里不安全。

事件是:

Each event is passed the Mapper, the mapped object itself, and the Connection which is being used to emit an INSERT, UPDATE or DELETE statement. 这些事件的吸引力是很明显的,因为如果一个应用程序想要绑定一些活动到一个特定类型的对象被INSERT持久化时,钩子是非常具体的;与SessionEvents.before_flush()事件不同,不需要像Session.new那样搜索集合来查找目标。但是,当调用这些事件时,表示每个要发送的每个INSERT,UPDATE,DELETE语句的完整列表的清空计划已经已经决定,并且在此阶段不可以做出改变。因此,给定对象所能做的唯一改变是属性local到对象的行。对象或其他对象的任何其他更改都将影响Session的状态,这将无法正常工作。

这些映射级别的持久性事件中不支持的操作包括:

  • Session.add()
  • Session.delete()
  • 映射集合追加,添加,删除,删除,丢弃等
  • 映射的关系属性set / del事件,即someobject.related = someotherobject

传递Connection的原因是鼓励简单的SQL操作直接在Connection上进行,例如递增计数器或在日志表中插入额外的行。在处理Connection时,预计将使用核心级SQL操作;例如那些在SQL Expression Language Tutorial中描述的那些。

也有许多每个对象操作,根本不需要在flush事件中处理。最常见的方法是简单地在其__init__()方法内建立附加状态以及对象,例如创建与新对象关联的附加对象。使用Simple Validators中描述的验证器是另一种方法;这些函数可以拦截对属性的更改,并在响应属性更改时在目标对象上建立额外的状态更改。通过这两种方法,物体在进入冲洗步骤之前处于正确的状态。

对象生命周期事件

事件的另一个用例是跟踪对象的生命周期。这是指在Quickie Intro to Object States中首次引入的状态。

版本1.1中的新增功能:添加了一个事件系统,用于拦截Session中所有可能的对象状态转换。

上述所有状态都可以通过事件进行充分跟踪。每个事件代表一个独特的状态转换,意思是起始状态和目标状态都是被跟踪的部分。除了最初的瞬态事件之外,所有事件都是以Session对象或类的形式出现的,这意味着它们可以与特定的Session对象相关联:

from sqlalchemy import event
from sqlalchemy.orm import Session

session = Session()

@event.listens_for(session, 'transient_to_pending')
def object_is_pending(session, obj):
    print("new pending: %s" % obj)

或者Session类本身以及特定的sessionmaker,这可能是最有用的形式:

from sqlalchemy import event
from sqlalchemy.orm import sessionmaker

maker = sessionmaker()

@event.listens_for(maker, 'transient_to_pending')
def object_is_pending(session, obj):
    print("new pending: %s" % obj)

听众当然可以堆叠在一个功能的顶部,这很可能是常见的。例如,要跟踪进入持久状态的所有对象:

@event.listens_for(maker, "pending_to_persistent")
@event.listens_for(maker, "deleted_to_persistent")
@event.listens_for(maker, "detached_to_persistent")
@event.listens_for(maker, "loaded_as_persistent")
def detect_all_persistent(session, instance):
    print("object is now persistent: %s" % instance)

瞬态¶ T0>

所有映射对象在第一次构建时都以transient开始。在这种状态下,对象单独存在,并且与任何Session没有关联。For this initial state, there’s no specific “transition” event since there is no Session, however if one wanted to intercept when any transient object is created, the InstanceEvents.init() method is probably the best event. 此事件适用于特定的类或超类。例如,要拦截特定声明基的所有新对象:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import event

Base = declarative_base()

@event.listens_for(Base, "init", propagate=True)
def intercept_init(instance, args, kwargs):
    print("new transient: %s" % instance)

瞬态到等待

The transient object becomes pending when it is first associated with a Session via the Session.add() or Session.add_all() method. 一个对象也可以成为Session的一部分,作为“cascade”从明确添加的引用对象中获得的结果。使用SessionEvents.transient_to_pending()事件可以检测到暂挂转换的暂态:

@event.listens_for(sessionmaker, "transient_to_pending")
def intercept_transient_to_pending(session, object_):
    print("transient to pending: %s" % object_)

等待持久

对于实例,当flush和INSERT语句发生时,pending对象变成persistent该对象现在拥有一个身份密钥。跟踪SessionEvents.pending_to_persistent()事件的挂起状态:

@event.listens_for(sessionmaker, "pending_to_persistent")
def intercept_pending_to_persistent(session, object_):
    print("pending to persistent: %s" % object_)

等待瞬变

The pending object can revert back to transient if the Session.rollback() method is called before the pending object has been flushed, or if the Session.expunge() method is called for the object before it is flushed. 使用SessionEvents.pending_to_transient()事件跟踪挂起到瞬态:

@event.listens_for(sessionmaker, "pending_to_transient")
def intercept_pending_to_transient(session, object_):
    print("transient to pending: %s" % object_)

加载为持久

从数据库加载时,对象可以直接以persistent状态出现在Session中。Tracking this state transition is synonymous with tracking objects as they are loaded, and is synonomous with using the InstanceEvents.load() instance-level event. 然而,SessionEvents.loaded_as_persistent()事件是以会话为中心的钩子提供的,以便在通过此特定途径进入持久状态时拦截对象:

@event.listens_for(sessionmaker, "loaded_as_persistent")
def intercept_loaded_as_persistent(session, object_):
    print("object loaded into persistent state: %s" % object_)

持续到瞬间

如果对象首次添加为待处理的事务调用Session.rollback()方法,持久对象可以恢复到瞬态状态。在ROLLBACK的情况下,使这个对象持久化的INSERT语句被回滚,并且该对象从Session被逐出以再次变为瞬态的。使用SessionEvents.persistent_to_transient()事件挂钩跟踪从持久性恢复为瞬态的对象:

@event.listens_for(sessionmaker, "persistent_to_transient")
def intercept_persistent_to_transient(session, object_):
    print("persistent to transient: %s" % object_)

持续删除

当标记为删除的对象在刷新过程中从数据库中删除时,持久对象进入deleted状态。请注意,这与为目标对象调用Session.delete()方法不一样。Session.delete()方法仅标记要删除的对象;直到刷新过程结束,才会发出实际的DELETE语句。在刷新之后,目标对象存在“已删除”状态。

在“已删除”状态下,对象仅与Session有少量关联。它不存在于身份映射中,也不存在于Session.deleted集合中,该集合指向何时待删除。

从“已删除”状态,对象可以在事务提交时转到分离状态,或者如果事务被回滚,则返回到持久状态。

使用SessionEvents.persistent_to_deleted()跟踪永久删除的转换:

@event.listens_for(sessionmaker, "persistent_to_deleted")
def intercept_persistent_to_deleted(session, object_):
    print("object was DELETEd, is now in deleted state: %s" % object_)

已删除分离

当会话的事务被提交时,被删除的对象变成detachedSession.commit()方法被调用之后,数据库事务是最终的,并且Session现在完全丢弃已删除的对象并删除所有的关联。使用SessionEvents.deleted_to_detached()跟踪已删除的分离转换:

@event.listens_for(sessionmaker, "deleted_to_detached")
def intercept_deleted_to_detached(session, object_):
    print("deleted to detached: %s" % object_)

注意

当对象处于已删除状态时,可以使用inspect(object).deleted访问的InstanceState.deleted属性返回True。但是当对象被分离时,InstanceState.deleted将再次返回False。要检测一个对象是否被删除,无论它是否被分离,使用InstanceState.was_deleted存取器。

持久分离

The persistent object becomes detached when the object is de-associated with the Session, via the Session.expunge(), Session.expunge_all(), or Session.close() methods.

注意

An object may also become implicitly detached if its owning Session is dereferenced by the application and discarded due to garbage collection. 在这种情况下,没有事件被发射

使用SessionEvents.persistent_to_detached()事件跟踪从持久移动到分离的对象:

@event.listens_for(sessionmaker, "persistent_to_detached")
def intecept_persistent_to_detached(session, object_):
    print("object became detached: %s" % object_)

分离到持久

当使用Session.add()或等效方法将分离的对象重新关联到会话时,分离的对象将变为持久对象。使用SessionEvents.detached_to_persistent()事件跟踪从分离状态移回到持久性状态的对象:

@event.listens_for(sessionmaker, "detached_to_persistent")
def intecept_detached_to_persistent(session, object_):
    print("object became persistent again: %s" % object_)

已删除到持久

当使用Session.rollback()方法回退已删除DELETEd的事务时,可以将deleted对象恢复到persistent状态。跟踪使用SessionEvents.deleted_to_persistent()事件回退到持久状态的已删除对象:

@event.listens_for(sessionmaker, "transient_to_pending")
def intercept_transient_to_pending(session, object_):
    print("transient to pending: %s" % object_)

事务事件

事务事件允许在Session级别发生事务边界时以及Session更改Connection对象上的事务性状态时通知应用程序。

属性更改事件

属性更改事件允许拦截对象上的特定属性何时被修改。这些事件包括AttributeEvents.set()AttributeEvents.append()AttributeEvents.remove()这些事件非常有用,特别是对于每个对象的验证操作;然而,使用一个“验证器”钩子在后台使用这些钩子通常要方便得多;请参阅Simple Validators以了解其背景。属性事件也是反向引用机制的后面。属性事件的使用示例在Attribute Instrumentation中。