使用非全局会话
有时最好不要使用SQLAlchemy的线程作用域会话(例如,当您需要在异步系统中使用Pyramid时)。幸运的是,这样做很容易。您可以将会话工厂存储在应用程序的注册表中,并调用会话工厂作为向请求对象询问属性的副作用。然后,会话对象的生存期将与请求的生存期匹配。
我们将使用Configurator.add_request_method添加SQLAlchemy会话来请求对象并Request.add_finished_callback关闭该会话。
我们假设您有一个.ini文件,其sqlalchemy.设置可以正确指定数据库:
现在,SQLAlchemy会话在视图代码中以request.db或 可用config.registry.dbmaker()。
# __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:
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包装内的模型 。
扫码关注更多精彩
点亮在看,你最好看!