版本: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 或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()方法。

At this point, the scoped_session object is “empty”, and will create a new Session when called again. 如下图所示,这与我们之前的Session不一样:

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

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

隐式方法访问

scoped_session的工作很简单,所有要求的人都要持有一个SessionAs a means of producing more transparent access to this Session, the scoped_session also includes proxy behavior, meaning that the registry itself can be treated just like a Session directly; when methods are called on this object, they are proxied to the underlying Session being maintained by the registry:

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实例。

Using this technique, the scoped_session provides a quick and relatively simple (if one is familiar with thread-local storage) way of providing a single, global object in an application that is safe to be called upon from multiple threads.

与往常一样,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框架的事件系统集成来建立“on request end”事件。

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

参数:
  • T0> session_factory T1> ¶ T2> - 一个工厂来创建新Session这通常是但不一定是sessionmaker的一个实例。
  • scopefunc – optional function which defines the current scope. 如果不通过,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__(createfunc, scopefunc)

构建一个新的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