【问题标题】:How to work with multiple databases with Python Pyramid如何使用 Python Pyramid 处理多个数据库
【发布时间】:2016-03-14 13:45:55
【问题描述】:

我想使用 Python Pyramid Framework 和 SQL Alchemy 处理多个数据库。

我有 1 个包含用户信息的数据库,以及多个存储应用程序信息的数据库(具有相同的结构)。每个用户在登录时选择一个数据库,并且只显示来自该数据库(而不是其他数据库)的信息。

我应该如何构建我的代码?

我正在考虑将 dbname 保存在会话中,并在每次请求时检查所选数据库的用户权限并生成一个新的 db 会话。所以我的观点看起来像(伪代码):

@view_config(route_name='home', renderer='json')

def my_view_ajax(request):

    try:

        database = int(request.GET['database'])

        # check user permissions from user database

        engine = create_engine('postgresql://XXX:XXX@localhost/'+database)

        DBSession.configure(bind=engine)

        items = DBSession.query('table').all()

    except DBAPIError:

        return 'error'

    return items

我应该使用每个请求的用户信息生成一个新的数据库会话吗?还是有更好的办法?

谢谢

【问题讨论】:

  • 在每个请求上创建一个新连接可能会非常昂贵,尽管这取决于您正在编写的 API 的类型 - 如果它触发一个需要几分钟才能完成的进程,那么它会我猜可以忽略不计。前段时间我问过一个类似的问题:stackoverflow.com/questions/13372001/… - 最终使用了具有多个命名空间的单个数据库,每个用户帐户一个。
  • 我喜欢你的方法,我会试一试,谢谢。

标签: python database sqlalchemy pyramid


【解决方案1】:

这在 Pyramid+SQLAlchemy 中很容易做到,但你可能想要 切换到更重的样板,更手动的会话管理样式,并且您将希望了解 SQLA 的会话管理文档,因为在处理多个并发会话时很容易出错。此外,诸如连接管理之类的东西应该远离视图,并且应该存在于服务器启动生命周期中的组件中,并在请求线程之间共享。如果您在 Pyramid 中正确执行此操作,您的视图应该非常小,并且您应该有许多通过 ZCA(注册表)协同工作的组件。

在我的应用程序中,我有一个 db 工厂对象,当请求它们时会获取会话,我在服务器启动代码中实例化这些对象(__ init __.py 中的内容)并将它们保存在注册表中。然后,您可以使用 reify 装饰器将每个 db 的会话附加到您的请求对象,还可以附加一个请求清理方法的内部管理结束以关闭它们。这可以通过自定义请求工厂或直接从 init 附加到请求的方法来完成,我个人最终使用自定义工厂,因为我发现它更易于阅读并且我通常最终会在那里添加更多内容。

# our DBFactory component, from some model package
class DBFactory(object):

    def __init__(self, db_url, **kwargs):
        db_echo = kwargs.get('db_echo', False)
        self.engine = create_engine(db_url, echo=db_echo)

        self.DBSession = sessionmaker(autoflush=False)
        self.DBSession.configure(bind=self.engine)
        self.metadata = Base.metadata
        self.metadata.bind = self.engine

    def get_session(self):
        session = self.DBSession()
        return session


# we instantiate them in the __init__.py file, and save on registry
def main(global_config, **settings):
    """runs on server start, returns a Pyramid WSGI application  """

    config = Configurator(
        settings=settings,
        # ask for a custom request factory
        request_factory = MyRequest,
    )

    config.registry.db1_factory = DBFactory( db_url=settings['db_1_url'] )
    config.registry.db2_factory = DBFactory( db_url=settings['db_2_url'] )


# and our custom request class, probably in another file
class MyRequest(Request):
    "override the pyramid request object to add explicit db session handling"

    @reify
    def db1_session(self):
        "returns the db_session at start of request lifecycle"
        # register callback to close the session automatically after
        # everything else in request lifecycle is done
        self.add_finished_callback( self.close_dbs_1 )
        return self.registry.db1_factory.get_session()

    @reify
    def db2_session(self):
        self.add_finished_callback( self.close_dbs_2 )
        return self.registry.db2_factory.get_session()

    def close_dbs_1(self, request):
        request.db1_session.close()

    def close_dbs_2(self, request):
        request.db2_session.close()


# now view code can be very simple    
def my_view(request):
    # get from db 1
    stuff = request.db1_session.query(Stuff).all()
    other_stuff = request.db2_session.query(OtherStuff).all()
    # the above sessions will be closed at end of request when 
    # pyramid calls your close methods on the Request Factory

    return Response("all done, no need to manually close sessions here!")

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-08-18
    • 2014-06-26
    • 2010-12-12
    • 1970-01-01
    • 1970-01-01
    • 2022-01-03
    • 2015-04-20
    • 1970-01-01
    相关资源
    最近更新 更多