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

SQLAlchemy 1.1文档

上下文/线程本地会话

回想一下When do I construct a Session, when do I commit it, and when do I close it?,引入了“session scopes”的概念,重点放在web应用程序和练习将Session的作用域与Web请求的作用域相关联。大多数现代Web框架都包含集成工具,因此可以自动管理Session的范围,并且应该尽可能使用这些工具。

SQLAlchemy包括其自己的帮助对象,这有助于建立用户定义的会话范围。它也被第三方集成系统用来帮助构建他们的集成方案。

该对象是scoped_session对象,它表示Session对象的注册表如果您不熟悉注册表模式,可以在企业架构模式中找到一个很好的介绍。

注意

scoped_session对象是许多SQLAlchemy应用程序使用的非常流行和有用的对象。然而,重要的是要注意,它只向会话管理的问题提出一种方法如果您是SQLAlchemy的新手,特别是如果术语“线程局部变量”对您来说看起来很陌生,我们建议您尽可能先熟悉一个现成的集成系统,例如Flask-SQLAlchemy < / t0>或zope.sqlalchemy

通过调用它来构造一个scoped_session,传递一个工厂,可以创建新的Session对象。一个工厂只是在调用时产生一个新的对象,而在Session的情况下,最常见的工厂就是本节前面介绍的sessionmaker。 下面我们来说明这个用法:

>>> from sqlalchemy.orm import scoped_session
>>> from sqlalchemy.orm import sessionmaker

>>> session_factory = sessionmaker(bind=some_engine)
>>> Session = scoped_session(session_factory)

当我们“调用”注册表时,我们创建的scoped_session对象现在将调用sessionmaker

>>> some_session = Session()

以上,some_sessionSession的一个实例,我们现在可以使用它来与数据库通信。同样的Session也出现在我们创建的scoped_session注册表中。如果我们再次调用注册表,我们会返回相同的 Session

>>> some_other_session = Session()
>>> some_session is some_other_session
True

该模式允许应用程序的不同部分调用全局scoped_session,以便所有这些区域可能共享相同的会话,而无需明确地传递。我们在注册表中建立的Session将保持,直到我们通过调用scoped_session.remove()明确告诉我们的注册表处理它:

>>> Session.remove()

scoped_session.remove()方法首先在当前的会话上调用Session.close(),这样可以释放任何连接/事务首先由会话拥有的资源,然后丢弃会话本身。这里的“释放”意味着连接将返回到其连接池,并且任何事务状态都将回滚,最终使用底层DBAPI连接的rollback()方法。

此时,scoped_session对象为“空”,并在再次调用时创建 Session如下图所示,这与我们之前的Session不一样:

>>> new_session = Session()
>>> new_session is some_session
False

上述一系列步骤简要说明了“注册表”模式的概念。有了这个基本想法,我们可以讨论这种模式如何进行的一些细节。

隐式方法访问

scoped_session的工作很简单;为所有需要的人保留一个Session作为对这个Session产生更多透明访问的手段,scoped_session还包含代理行为,这意味着注册表本身可以被视为类似直接Session;当在这个对象上调用方法时,它们被代理到由注册表维护的基础Session

Session = scoped_session(some_factory)

# equivalent to:
#
# session = Session()
# print(session.query(MyClass).all())
#
print(Session.query(MyClass).all())

上面的代码通过调用注册表来完成与获取当前Session相同的任务,然后使用该Session

线程本地作用域

熟悉多线程编程的用户会注意到,将任何东西表示为全局变量通常是一个坏主意,因为它意味着全局对象将被许多线程同时访问。Session对象完全被设计为以非并发方式使用,就多线程而言意味着“一次只能在一个线程中”。So our above example of scoped_session usage, where the same Session object is maintained across multiple calls, suggests that some process needs to be in place such that mutltiple calls across many threads don’t actually get a handle to the same session. 我们称之为线程本地存储,这意味着将使用一个特殊的对象来维护每个应用程序线程的独特对象。Python通过threading.local()结构提供了这个功能。The scoped_session object by default uses this object as storage, so that a single Session is maintained for all who call upon the scoped_session registry, but only within the scope of a single thread. 在另一个线程中调用注册表的调用者将获得该另一个线程本地的Session实例。

使用这种技术,scoped_session提供了一个快速且相对简单的方法(如果人们熟悉线程本地存储),在应用程序中提供单个全局对象的方式可以安全地从多个线程。

与往常一样,scoped_session.remove()方法会移除与该线程关联的当前Session(如果有的话)。但是,threading.local()对象的一个​​优点是,如果应用程序线程本身结束,那么该线程的“存储”也会被垃圾收集。因此,使用线程本地作用域和一个生成并拆除线程的应用程序实际上是“安全的”,而不需要调用scoped_session.remove()然而,事务本身的范围,即通过Session.commit()Session.rollback()结束它们通常仍然是必须明确安排在适当的时间,除非应用程序实际上将线程的生命周期与事务的生命周期相关联。

在Web应用程序中使用线程本地作用域

正如在When do I construct a Session, when do I commit it, and when do I close it?,Web应用程序围绕Web请求的概念构建 t2>,并且将这样的应用程序与Session集成通常意味着Session将与该请求相关联。事实证明,大多数Python Web框架(异常框架Twisted和Tornado等显着异常)都以简单的方式使用线程,以便在一个工作线程当请求结束时,工作线程被释放到可用于处理另一请求的工作者池中。

Web请求和线程的这种简单对应意味着将Session与线程相关联意味着它也与该线程内运行的Web请求相关联,反之亦然,前提是Session因此,使用scoped_session作为将Session与Web应用程序集成的快速方法是一种常见做法。下面的序列图说明了这个流程:

Web Server          Web Framework        SQLAlchemy ORM Code
--------------      --------------       ------------------------------
startup        ->   Web framework        # Session registry is established
                    initializes          Session = scoped_session(sessionmaker())

incoming
web request    ->   web request     ->   # The registry is *optionally*
                    starts               # called upon explicitly to create
                                         # a Session local to the thread and/or request
                                         Session()

                                         # the Session registry can otherwise
                                         # be used at any time, creating the
                                         # request-local Session() if not present,
                                         # or returning the existing one
                                         Session.query(MyClass) # ...

                                         Session.add(some_object) # ...

                                         # if data was modified, commit the
                                         # transaction
                                         Session.commit()

                    web request ends  -> # the registry is instructed to
                                         # remove the Session
                                         Session.remove()

                    sends output      <-
outgoing web    <-
response

使用上述流程,将Session与Web应用程序集成的过程有两个要求:

  1. 首次启动Web应用程序时,创建一个scoped_session注册表,确保该对象可由应用程序的其余部分访问。
  2. 确保在Web请求结束时调用scoped_session.remove(),通常通过与Web框架的事件系统集成以建立“请求结束”事件。

As noted earlier, the above pattern is just one potential way to integrate a Session with a web framework, one which in particular makes the significant assumption that the web framework associates web requests with application threads. It is however strongly recommended that the integration tools provided with the web framework itself be used, if available, instead of scoped_session.

In particular, while using a thread local can be convenient, it is preferable that the Session be associated directly with the request, rather than with the current thread. 自定义作用域的下一部分详细介绍了一种更高级的配置,它可以将scoped_session的用法与基于直接请求的作用域或任何类型的作用域相结合。

使用自定义创建的范围

scoped_session对象的“线程本地”范围的默认行为只是“范围”Session的众多选项之一。自定义范围可以基于任何现有的“我们正在处理的事物”的系统来定义。

假设一个web框架定义了一个库函数get_current_request()使用此框架构建的应用程序可以随时调用此函数,并且结果将是表示当前正在处理的请求的某种Request对象。如果Request对象是可散列的,那么这个函数可以很容易地与scoped_session集成以将Session与请求相关联。下面我们结合Web框架on_request_end提供的假设事件标记来说明这一点,该请求允许在请求结束时调用代码:

from my_web_framework import get_current_request, on_request_end
from sqlalchemy.orm import scoped_session, sessionmaker

Session = scoped_session(sessionmaker(bind=some_engine), scopefunc=get_current_request)

@on_request_end
def remove_session(req):
    Session.remove()

在上面,我们以通常的方式实例化scoped_session,不同之处在于我们将我们的请求返回函数作为“scopefunc”传递。这指示scoped_session在调用注册表返回当前Session时使用此函数生成字典密钥。在这种情况下,我们确保实施可靠的“删除”系统尤为重要,因为本字典不能自行管理。

上下文会话API

class sqlalchemy.orm.scoping。 scoped_session session_factoryscopefunc =无 T5> ) T6> ¶ T7>

提供Session对象的范围管理。

有关教程,请参阅Contextual/Thread-local Sessions

__呼叫__ T0> ( T1> **千瓦 T2> ) T3> ¶ T4>

返回当前Session,如果不存在,则使用scoped_session.session_factory创建它。

参数:**kw – Keyword arguments will be passed to the scoped_session.session_factory callable, if an existing Session is not present. 如果存在Session并且关键字参数已被传递,则引发InvalidRequestError
__ init __ session_factoryscopefunc = None t5 >

构建一个新的scoped_session

参数:
  • session_factory – a factory to create new Session instances. 这通常但不一定是sessionmaker的一个实例。
  • scopefunc - 定义当前范围的可选函数。如果不通过,scoped_session对象将采用“线程本地”作用域,并将使用Python threading.local()来维护当前的Session如果通过,函数应该返回一个可哈希标记;此标记将用作字典中的键以存储和检索当前的Session
配置 T0> ( T1> ** kwargs T2> ) T3> ¶ T4>

重新配置这个scoped_session使用的sessionmaker

参见sessionmaker.configure()

query_property T0> ( T1> query_cls =无 T2> ) T3> ¶ T4>

返回一个类属性,它在调用时针对类和当前Session生成一个Query对象。

例如。:

Session = scoped_session(sessionmaker())

class MyClass(object):
    query = Session.query_property()

# after mappers are defined
result = MyClass.query.filter(MyClass.name=='foo').all()

默认生成会话配置的查询类的实例。要覆盖和使用自定义实现,请提供一个query_cls可调用。可调用对象将作为位置参数和会话关键字参数与类的映射器一起调用。

放置在类上的查询属性的数量没有限制。

除去 T0> ( T1> ) T2> ¶ T3>

如果存在,则丢弃当前的Session

这将首先在当前的Session上调用Session.close()方法,该方法释放仍然保存的任何现有的事务/连接资源;交易具体回滚。然后Session被丢弃。在相同范围内的下一次使用时,scoped_session将生成一个新的Session对象。

session_factory =无

提供给__ init __session_factory存储在此属性中,并可以在稍后访问。当需要新的非范围SessionConnection到数据库时,这非常有用。

class sqlalchemy.util.ScopedRegistry(createfunc, scopefunc)

基于“范围”功能可以存储单个类的一个或多个实例的注册表。

该对象将__call__实现为“getter”,因此通过调用myregistry(),将为当前范围返回包含的对象。

参数:
  • createfunc – a callable that returns a new object to be placed in the registry
  • scopefunc – a callable that will return a key to store/retrieve an object.
__ init __ createfuncscopefunc

构建一个新的ScopedRegistry

参数:
  • createfunc – A creation function that will generate a new value for the current scope, if none is present.
  • scopefunc – A function that returns a hashable token representing the current scope (such as, current thread identifier).
明确 T0> ( T1> ) T2> ¶ T3>

清除当前的范围,如果有的话。

具有 T0> ( T1> ) T2> ¶ T3>

如果当前范围中存在对象,则返回True。

设置 T0> ( T1> OBJ T2> ) T3> ¶ T4>

设置当前范围的值。

class sqlalchemy.util。 ThreadLocalRegistry createfunc ) t5 > ¶ T6>

基础:sqlalchemy.util._collections.ScopedRegistry

一个使用threading.local()变量进行存储的ScopedRegistry