我想与Flask和SQLAlchemy一起使用显式的master-master数据库设置,希望Flask-SQLAlchemy支持这一点。
我希望能够执行类似下面的代码片段,但我不确定Flask-SQLAlchemy是否支持
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
SQLALCHEMY_DATABASE_URI = 'default_DB_uri'
SQLALCHEMY_BINDS = { 'master1':'first_master_DB_uri', 'master2': 'second_master_DB_uri' }
app.config['SQLALCHEMY_DATABASE_URI'] = SQLALCHEMY_DATABASE_URI
app.config['SQLALCHEMY_BINDS'] = SQLALCHEMY_BINDS
db = SQLAlchemy(app)
@app.route('/some_endpoint')
def some_endpoint():
# read some data for the default DB
readData = db.session.query('select ...')
m = SomeModel()
masterSession1 = db.session(bind='master1')
# persist the data in m into the first master
masterSession1.add(m)
masterSession2 = db.session(bind='master2')
# persist the data into the second master
masterSession2.add(m)
return "some return value"
有没有办法使用Flask-SQLAlchemy实现这一点并绑定? 我想Flask-SQLAlchemy已经使用绑定处理了多个引擎但是我看不到如何使用它来进行显式数据库选择而不是像这里提到的基于模型的选择:http://pythonhosted.org/Flask-SQLAlchemy/binds.html
感谢您的帮助。
答案 0 :(得分:1)
以下代码是我最终拥有此功能的代码。
一些注意事项:
get_table_for_bind
以将所有表绑定到所有绑定而没有明确的__bind_key__
。这样做是为了能够调用db.create_all()
或db.drop_all()
并在所有DB中创建/删除表。为了使其工作而不中断默认数据库选择,在未指定特定绑定时,get_binds
已更改为在原始实现后再次映射None
绑定,以覆盖表 - >绑定映射。using_bind
,则应使用默认数据库。from flask_sqlalchemy import SQLAlchemy, SignallingSession, get_state
from flask_sqlalchemy._compat import itervalues
class UsingBindSignallingSession(SignallingSession):
def get_bind(self, mapper=None, clause=None):
if self._name:
_eng = get_state(self.app).db.get_engine(self.app,bind=self._name)
return _eng
else:
return super(UsingBindSignallingSession, self).get_bind(mapper, clause)
_name = None
def using_bind(self, name):
self._name = name
return self
class UsingBindSQLAlchemy(SQLAlchemy):
def create_session(self, options):
return UsingBindSignallingSession(self, **options)
def get_binds(self, app=None):
retval = super(UsingBindSQLAlchemy, self).get_binds(app)
# get the binds for None again in order to make sure that it is the default bind for tables
# without an explicit bind
bind = None
engine = self.get_engine(app, bind)
tables = self.get_tables_for_bind(bind)
retval.update(dict((table, engine) for table in tables))
return retval
def get_tables_for_bind(self, bind=None):
"""Returns a list of all tables relevant for a bind.
Tables without an explicit __bind_key__ will be bound to all binds.
"""
result = []
for table in itervalues(self.Model.metadata.tables):
# if we don't have an explicit __bind_key__ bind this table to all databases
if table.info.get('bind_key') == bind or table.info.get('bind_key') == None:
result.append(table)
return result
db = UsingBindSQLAlchemy()
# This is the default DB
SQLALCHEMY_DATABASE_URI=YOUR_MAIN_DB_URI_CONNECT_STRING
# Master1 and Master2
SQLALCHEMY_BINDS = { 'master1':YOUR_MASTER1_DB_URI_CONNECT_STRING, 'master2':YOUR_MASTER2_DB_URI_CONNECT_STRING }
# Tables without __bind_key__ will be dropped/created on all DBs (default, master1, master2)
db.drop_all()
db.create_all()
s = db.session().using_bind('master1')
s.add(SOME_OBJECT)
s.commit()
s = db.session().using_bind('master2')
s.add(SOME_OBJECT_CLONE) # a clone of the original object, before the first add()
s.commit()
# and the default DB, as always
db.session.add(SOME_OTHER_OBJECT)
db.session.commit()