用多个数据库提供页面
我现在有:多个一模一样的数据库和一个SQL-Alchemy的设置。
我想要实现的目标是:有一个“Flask”函数能够查询所有的数据库。其他的函数只需要访问其中一个数据库。
这些数据库已经存在,我正在从中加载数据。
最开始我只是使用SQL-Alchemy的抽象层,没有使用任何Flask模型。代码大概是这样的:
app = Flask(__name__)
app.config.from_object('myflaskapp.config.settings')
metadata = None
def connect_db():
engine = create_engine(app.config['DATABASE_URI'])
global metadata
metadata = MetaData(bind=engine)
return engine.connect()
@app.before_request
def before_request():
g.db = connect_db()
@app.after_request
def after_request(response):
close_connection()
return response
其中DATABASE_URI是在settings.py中定义的。我想这没什么关系,不过底层的数据库是一个Mysql服务器,DATABASE_URI看起来像这样:
DATABASE_URI = 'mysql://' + dbuser + ':' + dbpass + '@' + dbhost + '/' +dbname
上面的代码让我可以写出类似这样的代码:
myTable = Table('branch', metadata, autoload=True)
myBranch = select([myTable])
这非常方便。这个方法运行得很好,前提是我只处理一个数据库。不过,如果我需要处理多个数据库,就不太行了。更准确地说,我想在同一个函数中显示属于多个数据库的数据,而这些数据库的结构完全相同。也就是说,针对任何一个数据库都可以成功运行相同的查询。用伪代码表示大概是这样的:
@app.route('/summary')
def summary():
....
allData = []
for db in all_of_my_dbs:
allData.append(db.query())
....
sendToTemplate(allData)
这能实现吗?可行吗?谢谢。
3 个回答
看起来这个问题上有个小bug - https://github.com/mitsuhiko/flask-sqlalchemy/pull/222。问题在于,绑定的键必须是唯一的,并且只能用于一个类(至少目前是这样)。所以你可以有多个数据库,但每个数据库只能有一个表或一个类。不过,如果你有很多表的话,这样做会消耗很多资源,暂时这只是个权宜之计……
这里是关于“绑定”的参考资料 - http://flask-sqlalchemy.pocoo.org/2.0/binds/#binds
下面是配置的内容:
SQLALCHEMY_DATABASE_URI = 'sqlite://///main.db'
SQLALCHEMY_BINDS= {
'table1':'sqlite://///DB1.db',
'table2':'sqlite://///DB2.db',
}
下面是模型的定义:
class table1(db.Model):
__tablename__ = "table1"
__bind_key__='table1'
class table2(db.Model):
__tablename__ = "table2"
__bind_key__='table2'
首先,我想说说我自己的情况。这里有两个表,它们的名字和结构是一样的,但它们属于不同的数据库或模式。
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
databases = {
'sqlite1': 'sqlite:///sqlite1.db',
'sqlite2': 'sqlite:///sqlite2.db'
}
app.config['SQLALCHEMY_BINDS'] = databases
db = SQLAlchemy(app)
class Thing(db.Model):
__tablename__ = "mytable"
__bind_key__ = 'sqlite1'
thing_id = db.Column(db.Integer, primary_key=True)
thing_val = db.Column(db.String(40))
def __init__(self, thing_val):
self.thing_val = thing_val
if __name__ == "__main__":
t1 = Thing.query.filter_by(thing_id=1).first()
print t1.name
当我在第一个类定义后面添加第二个类,叫做Thing2的时候,一切就崩溃了。也就是说:
class Thing(db.Model):
__tablename__ = "mytable"
__bind_key__ = 'sqlite1'
thing_id = db.Column(db.Integer, primary_key=True)
thing_val = db.Column(db.String(40))
def __init__(self, thing_val):
self.thing_val = thing_val
class Thing2(db.Model):
__tablename__ = "mytable"
__bind_key__ = 'sqlite2'
thing_id = db.Column(db.Integer, primary_key=True)
thing_val = db.Column(db.String(40))
def __init__(self, thing_val):
self.thing_val = thing_val
上面的代码出错了,错误信息是:
InvalidRequestError: 表 'mytable' 已经在这个MetaData实例中定义过了。请指定 'extend_existing=True' 来重新定义现有表对象的选项和列。
所以这个框架似乎无法区分这两个表。
这绝对是可能的。
试着在你的设置文件里添加类似下面的内容
SQLALCHEMY_BINDS = {
'users': 'mysqldb://localhost/users',
'appmeta': 'sqlite:////path/to/appmeta.db'
}
编辑
这里有一个可以直接复制粘贴的示例
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.debug = True
databases = {
'sqlite1': 'sqlite:///sqlite1.db',
'sqlite2': 'sqlite:///sqlite2.db'
}
app.config['SQLALCHEMY_BINDS'] = databases
db = SQLAlchemy(app)
class Thing(db.Model):
__tablename__ = "sqlite1"
thing_id = db.Column(db.Integer, primary_key=True)
thing_val = db.Column(db.String(40))
def __init__(self, thing_val):
self.thing_val = thing_val
class Thing2(db.Model):
__tablename__ = "sqlite2"
thing_id = db.Column(db.Integer, primary_key=True)
thing_val = db.Column(db.String(40))
def __init__(self, thing_val):
self.thing_val = thing_val
if __name__ == "__main__":
db.create_all()
t1 = Thing("test1")
t2 = Thing2("test2")
db.session.add(t1)
db.session.add(t2)
db.session.commit()
t1 = Thing.query.filter_by(thing_id=1).first()
t2 = Thing2.query.filter_by(thing_id=1).first()
print t1.thing_val
print t2.thing_val