SQL炼金术

it2024-10-09  36

使用非全局会话

有时最好不要使用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包装内的模型 。

扫码关注更多精彩

点亮在看,你最好看!

最新回复(0)