使用非全局会话
有时最好不要使用SQLAlchemy的线程作用域会话(例如,当您需要在异步系统中使用Pyramid时)。幸运的是,这样做很容易。您可以将会话工厂存储在应用程序的注册表中,并调用会话工厂作为向请求对象询问属性的副作用。然后,会话对象的生存期将与请求的生存期匹配。
我们将使用Configurator.add_request_method添加SQLAlchemy会话来请求对象并Request.add_finished_callback关闭该会话。
我们假设您有一个.ini文件,其sqlalchemy.设置可以正确指定数据库:
现在,SQLAlchemy会话在视图代码中以request.db或 可用config.registry.dbmaker()。
代码语言:javascript复制# __init__.py
from pyramid.config import Configurator
from sqlalchemy import engine_from_config
from sqlalchemy.orm import sessionmaker
def db(request):
maker = request.registry.dbmaker
session = maker()
def cleanup(request):
if request.exception is not None:
session.rollback()
else:
session.commit()
session.close()
request.add_finished_callback(cleanup)
return session
def main(global_config, **settings):
config = Configurator(settings=settings)
engine = engine_from_config(settings, prefix='sqlalchemy.')
config.registry.dbmaker = sessionmaker(bind=engine)
config.add_request_method(db, reify=True)
# .. rest of configuration ...
导入所有SQLAlchemy模型
如果您使用粘贴程序模板创建了Pyramid项目,则默认情况下,SQLAlchemy模型将驻留在单个文件中。这只是按照惯例。如果您希望有一个用于SQLAlchemy模型的目录而不是一个文件,那么您当然可以创建一个充满模型模块的Python包,将models.py文件替换models为Python包的目录(其中包含的目录__init__.py),例如每个 修改包的结构。但是,由于SQLAlchemy的“声明式”配置模式的行为,必须先导入所有保存活动SQLAlchemy模型的模块,然后才能成功使用它们。因此,如果您使用具有声明性基础的模型类,则需要找出一种方法来导入所有模型模块,以便能够在应用程序中使用它们。
您可能首先创建一个models目录,替换该models.py 文件,然后在其中创建一个名为的文件models/__init__.py。此时,您可以添加名为的子模块models/mymodel.py,其中包含一个 MyModel模型类。在models/__init__.py将定义声明性基类和全局DBSession对象,其中每个子模块模型(如models/mymodel.py)将需要导入。然后,您所需要做的就是在中添加每个子模块的导入models/__init__.py。
但是,当您将models包子模块import语句添加到时 models/__init__.py,这将导致循环导入依赖关系。该 models/__init__.py模块的进口mymodel和models/mymodel.py 进口models包。下次尝试启动您的应用程序时,由于这种循环依赖性,它会因导入错误而失败。
Pylons 1通过创建一个models/meta.py模块来解决此问题,在该模块中创建DBSession和声明性基础对象。该 models/__init__.py文件的每个子模块models的进口 DBSession和declarative_base从它。每当您.py 在models包中创建文件时,都希望为其添加导入 models/__init__.py。主程序将导入models包,这具有确保已导入所有模型类的副作用。您也可以执行此操作,效果很好。
但是,您可以交替使用config.scan()它的副作用。使用config.scan()可以避免之间的麻烦, models/__init__.py而models/themodel.py无需创建特殊的 models/meta.py。
例如,如果您在中执行此操作myapp/models/__init__.py:
代码语言:javascript复制from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
DBSession = scoped_session(sessionmaker())
Base = declarative_base()
def initialize_sql(engine):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
而在myapp/models/mymodel.py
:
from myapp.models import Base
from sqlalchemy import Column
from sqlalchemy import Unicode
from sqlalchemy import Integer
class MyModel(Base):
__tablename__ = 'models'
id = Column(Integer, primary_key=True)
name = Column(Unicode(255), unique=True)
value = Column(Integer)
def __init__(self, name, value):
self.name = name
self.value = value
而在myapp/__init__.py
:
from sqlalchemy import engine_from_config
from myapp.models import initialize_sql
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
config.scan('myapp.models') # the "important" line
engine = engine_from_config(settings, 'sqlalchemy.')
initialize_sql(engine)
# other statements here
config.add_handler('main', '/{action}',
'myapp.handlers:MyHandler')
return config.make_wsgi_app()
上面的重要行是config.scan('myapp.models')。config.scan 具有对给定的程序包名称进行递归导入的副作用。此副作用可确保myapp.models导入其中的每个文件,而无需您在其中“手工”导入每个 文件models/__init__.py。但是,它不会导入任何不在myapp.models包装内的模型 。