在多租户应用程序中动态设置 Flask-SQLAlchemy 数据库连接

Posted

技术标签:

【中文标题】在多租户应用程序中动态设置 Flask-SQLAlchemy 数据库连接【英文标题】:Dynamically setting Flask-SQLAlchemy database connection in multi-tenant app 【发布时间】:2015-06-22 01:25:19 【问题描述】:

我有一个“多租户Flask网络应用程序,它与 1 个“主”mysql 数据库(用于查找客户端信息)和数十个“客户端” MySQL 数据库(都具有相同的架构)。

我目前正在尝试使用 SQLAlchemyFlask-SQLAlchemy 扩展 来与数据库交互,但我正在努力寻找一种方法来允许我在我的应用中定义的模型动态地将上下文从一个客户端数据库切换到另一个,具体取决于客户端。

在Flask-SQLAlchemy site上,一个简单的例子如下所示:

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://username:password@Y.Y.Y.Y/db1'
db = SQLAlchemy(app)

class User(db.Model):
    # Etc.

唯一的问题是,SQLALCHEMY_DATABASE_URI 配置是静态完成的。我可能需要在 mysql://username:password@Y.Y.Y.Y/db1mysql://username:password@Z.Z.Z.Z/db1(或任何其他任意 MySQL URI)之间切换,具体取决于发出请求的客户端。

我发现了一些类似的问题(见下文),但在专门使用 Flask-SQLAlchemy 扩展时,我还没有找到一种干净的方法。

With sqlalchemy how to dynamically bind to database engine on a per-request basis

Flask SQLAlchemy setup dynamic URI

我还看到了一些为处理分片数据库提供的示例(这也应​​该适用,因为数据库本质上是由客户端在逻辑上分片的),但同样,这并不是 Flask-SQLAlchemy 特有的。

如果有意义,我也愿意直接使用 SQLAlchemy,无需 Flask-SQLAlchemy 扩展。我是 SQLAlchemy 的新手 - 任何帮助都将不胜感激!

编辑:能够反映数据库中的表模式将是一个奖励。

【问题讨论】:

你见过***.com/questions/7923966/…吗?如果是,你还有什么问题? @SeanVieira 谢谢 - 我刚刚检查了这个答案,这似乎是有道理的。我正在考虑为每个请求创建一个范围会话并将其存储在flask.g 中。我对如何设置课程本身感到困惑。在示例中,Userdb.Model 的子类,db 是从SQLALCHEMY_DATABASE_URI 初始化的。让User 从使用sqlalchemy.ext.declarative.declarative_base() 创建的基类继承也可以,或者我可以使用另一个类来代替不依赖于特定数据库配置的 Flask-SQLAlchemy 的db.Model 阅读这个:flask-sqlalchemy.pocoo.org/2.1/binds 然后看看这个:gist.github.com/adhorn/b84dc47175259992d406。 【参考方案1】:

您需要使用 SQLALCHEMY_BINDS 方法绑定到 mult[le 客户端数据库。

app.config['SQLALCHEMY_BINDS'] =  'user1': mysql database', user2: mysqal2database' 

您还需要引用模型中的 bind_key 名称,例如“user1”:

class users(db.Model):
   __bind_key__
   id = db.Column(Integer, primary_key=True)
   name = db.Column(String)

上述这些方法是您应该设置的主要 SQLALCHEMY_DATABASE_URI 的补充。虽然您可以在用户登录方法上动态绑定其他数据库。

希望对您有所帮助!

【讨论】:

【参考方案2】:

如果您使用的是 flask-sqlalchemy 0.12 或更高版本,则 BINDS 支持此功能。

SQLALCHEMY_DATABASE_URI = 'postgres://localhost/main'
SQLALCHEMY_BINDS = 
'users':        'mysqldb://localhost/users',
'appmeta':      'sqlite:////path/to/appmeta.db'

您可以在模型定义中指定连接数据库。

class User(db.Model):
    __bind_key__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)

它将使用 mysqldb auto。更多细节可以参考官方文档。 Multiple Databases with Binds

【讨论】:

这是垂直分区的正确答案,但我不认为你可以这样做水平。即,如果我的所有表格上都有一个client_id 列,并且我想要一个盒子上所有带有奇数的东西,而另一个盒子上所有带有偶数的东西,我认为绑定不会起作用。我需要 sqlalchemy.ext.horizo​​ntal_shard.ShardedSession【参考方案3】:

你可以做这样的事情。仅供参考,这是纯 SQLAlchemy,而不是 Flask-SQLAlchemy。希望你需要这个只读的东西。你可以找出一些其他的东西来写东西,比如你的会话/模型上的听众

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

# an Engine, which the Session will use for connection
# resources
some_engine_1 = create_engine('postgresql://scott:tiger@localhost/')
some_engine_2 = create_engine('postgresql://adriel:velazquez@localhost/')

# create a configured "Session" class
Session_1 = sessionmaker(bind=some_engine_1)
Session_2 = sessionmaker(bind=some_engine_2)

# create a Session
session_1 = Session_1()
session_2 = Session_2()

Base = declarative_base()

class ModelBase(Base):
    #different custom queries for each database
    master_query = session_1.query_property()
    client_1_query = session_2.query_property()


class User(ModelBase):
    pass
    #ETC


##Accessing the different databases:
User.master_query.filter(User.id == 1).all()
User.client_1_query.filter(User.id == 1).all()

【讨论】:

以上是关于在多租户应用程序中动态设置 Flask-SQLAlchemy 数据库连接的主要内容,如果未能解决你的问题,请参考以下文章

如何在多租户应用程序中更新所有租户的所有架构?

在多租户应用程序中如何检查应用程序在 Azure 门户中注册的租户数据?

如何在多租户环境中自动选择配置的 SAML 身份提供程序以使用 Spring SAML 进行 SSO

Spring在多租户环境中为占位符配置application.properties

在多租户 Hadoop 集群中管理 impala 资源的最佳方法是啥

是否有任何 PHP 框架(例如 CodeIgniter)支持基于每个用户帐户的数据库连接,以便在多租户数据库中使用?