flask_sqlalchemy對(duì)sqlalchemy的scoped_session進(jìn)行了封裝,使其session默認(rèn)為線程安全(scoped_session)方式。通過(guò)查詢?cè)创a我們發(fā)現(xiàn)了scoped_session的創(chuàng)建及工作原理。
sqlalchemy的session創(chuàng)建為工廠模式,及通過(guò)傳遞sessionmaker的函數(shù)調(diào)用,然后通過(guò)實(shí)現(xiàn)call方法實(shí)現(xiàn)其實(shí)例調(diào)用。這里對(duì)實(shí)際創(chuàng)建session的工廠Session類不做探究,只探究該session是如何通過(guò)工廠創(chuàng)建的。
我們一般使用db.session.query的方法查詢,在flask_sqlalchemy的SQLAlchemy類中實(shí)現(xiàn)了session的定義
class SQLAlchemy(object):
def __init__(self, app=None, use_native_unicode=True, session_options=None,
metadata=None, query_class=BaseQuery, model_class=Model,
engine_options=None):
self.session = self.create_scoped_session(session_options) # 創(chuàng)建session
# 省略其他代碼···
def create_scoped_session(self, options=None):
if options is None:
options = {}
# _app_ctx_stack.__ident_func__ 是指獲取當(dāng)前上下文的線程id
scopefunc = options.pop('scopefunc', _app_ctx_stack.__ident_func__)
options.setdefault('query_cls', self.Query)
return orm.scoped_session(
self.create_session(options), scopefunc=scopefunc
)
def create_session(self, options):
return orm.sessionmaker(class_=SignallingSession, db=self, **options)
這里可以看出create_session通過(guò)封裝orm.sessionmaker并將其傳遞給orm.scoped_session進(jìn)行創(chuàng)建,scoped_session接收了兩個(gè)參數(shù),分別為sessionmaker和scopefunc,scopefunc是通過(guò)flask應(yīng)用上下文生成線程id的調(diào)用。由scopefunc可以看出猜測(cè)scoped_session是線程安全的。orm.scoped_session是其核心,再看其實(shí)怎么實(shí)現(xiàn)的
sqlalchemy/orm/scoping.py
class scoped_session(object):
def __init__(self, session_factory, scopefunc=None):
self.session_factory = session_factory
if scopefunc:
self.registry = ScopedRegistry(session_factory, scopefunc)
else:
self.registry = ThreadLocalRegistry(session_factory)
def __call__(self, **kw):
if kw:
if self.registry.has():
raise sa_exc.InvalidRequestError(
"Scoped session is already present; "
"no new arguments may be specified."
)
else:
sess = self.session_factory(**kw)
self.registry.set(sess)
return sess
else:
return self.registry()
def remove(self):
if self.registry.has():
self.registry().close()
self.registry.clear()
def configure(self, **kwargs):
if self.registry.has():
warn(
"At least one scoped session is already present. "
" configure() can not affect sessions that have "
"already been created."
)
self.session_factory.configure(**kwargs)
def query_property(self, query_cls=None):
class query(object):
def __get__(s, instance, owner):
try:
mapper = class_mapper(owner)
if mapper:
if query_cls:
# custom query class
return query_cls(mapper, session=self.registry())
else:
# session's configured query class
return self.registry().query(mapper)
except orm_exc.UnmappedClassError:
return None
return query()
ScopedSession = scoped_session
def instrument(name):
def do(self, *args, **kwargs):
return getattr(self.registry(), name)(*args, **kwargs)
return do
for meth in Session.public_methods:
setattr(scoped_session, meth, instrument(meth))
def makeprop(name):
def set_(self, attr):
setattr(self.registry(), name, attr)
def get(self):
return getattr(self.registry(), name)
return property(get, set_)
for prop in (
"bind",
"dirty",
"deleted",
"new",
"identity_map",
"is_active",
"autoflush",
"no_autoflush",
"info",
"autocommit",
):
setattr(scoped_session, prop, makeprop(prop))
def clslevel(name):
def do(cls, *args, **kwargs):
return getattr(Session, name)(*args, **kwargs)
return classmethod(do)
for prop in ("close_all", "object_session", "identity_key"):
setattr(scoped_session, prop, clslevel(prop))
這里可以看出整個(gè)session的核心就是self.registry,幾乎所有的操作都是由self.registry進(jìn)行的。對(duì)于session的創(chuàng)建使用了工廠模式模式。
從ScopedRegistry的代碼可看出,當(dāng)我們調(diào)用了實(shí)例對(duì)象self.registry()的時(shí)候,如果當(dāng)前線程已經(jīng)綁定了session將其返回,如果沒(méi)有則調(diào)用了工廠去生成session,然后將其綁定到當(dāng)前線程上。
我覺(jué)得這段代碼最有意思的地方在于下面那三個(gè)對(duì)session對(duì)象上的方法與屬性的綁定。這也是我們?yōu)槭裁纯梢灾苯邮褂?code>db.session.query,而不是db.session().query
的原因,這里體現(xiàn)了代理模式的思想。
class ScopedRegistry(object):
def __init__(self, createfunc, scopefunc):
self.createfunc = createfunc
self.scopefunc = scopefunc
self.registry = {}
def __call__(self):
key = self.scopefunc()
try:
return self.registry[key]
except KeyError:
return self.registry.setdefault(key, self.createfunc())
下面的三個(gè)循環(huán)本別將一些屬性和操作綁定到了session上。
例如,Session.public_methods里面定義了query、commit、update等操作。當(dāng)調(diào)用session.query時(shí)先調(diào)用self.registry()去獲得session,再?gòu)漠?dāng)前session上獲取commit對(duì)象并調(diào)用。