Created
October 4, 2013 15:49
-
-
Save catermelon/6828122 to your computer and use it in GitHub Desktop.
Flask-SQLAlchemy - separating reads and writes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is not used unless SQLALCHEMY_BINDS is not present | |
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{username}:{password}@{hostname}/{database}?charset=utf8' | |
SQLALCHEMY_BINDS = { | |
'master': 'mysql+pymysql://{username}:{password}@{hostname}/{database}?charset=utf8', | |
'slave': 'mysql+pymysql://{username}:{password}@{hostname}/{database}?charset=utf8' | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from flask.ext.sqlalchemy import SQLAlchemy, get_state | |
import sqlalchemy.orm as orm | |
from functools import partial | |
import logging | |
log = logging.getLogger(__name__) | |
class AutoRouteSession(orm.Session): | |
def __init__(self, db, autocommit=False, autoflush=False, **options): | |
self.app = db.get_app() | |
self._model_changes = {} | |
orm.Session.__init__(self, autocommit=autocommit, autoflush=autoflush, | |
extension=db.session_extensions, | |
bind=db.engine, | |
binds=db.get_binds(self.app), **options) | |
def get_bind(self, mapper=None, clause=None): | |
try: | |
state = get_state(self.app) | |
except (AssertionError, AttributeError, TypeError) as err: | |
log.info("Unable to get Flask-SQLAlchemy configuration. Outputting default bind. Error:" + err) | |
return orm.Session.get_bind(self, mapper, clause) | |
# If there are no binds configured, connect using the default SQLALCHEMY_DATABASE_URI | |
if state is None or not self.app.config['SQLALCHEMY_BINDS']: | |
if not self.app.debug: | |
log.debug("Connecting -> DEFAULT. Unable to get Flask-SQLAlchemy bind configuration. Outputting default bind." ) | |
return orm.Session.get_bind(self, mapper, clause) | |
# Writes go to the master | |
elif self._flushing: # we who are about to write, salute you | |
log.debug("Connecting -> MASTER") | |
return state.db.get_engine(self.app, bind='master') | |
# Everything else goes to the slave | |
else: | |
log.debug("Connecting -> SLAVE") | |
return state.db.get_engine(self.app, bind='slave') | |
class AutoRouteSQLAlchemy(SQLAlchemy): | |
def create_scoped_session(self, options=None): | |
"""Helper factory method that creates a scoped session.""" | |
if options is None: | |
options = {} | |
scopefunc=options.pop('scopefunc', None) | |
return orm.scoped_session( | |
partial(AutoRouteSession, self, **options), scopefunc=scopefunc | |
) |
This can be done by declaring two models with the same name implementing two different db models.
We can also implement two databases with one table of the same table name.
An example can be like this:
app = Flask(__name__)
app.config['SQLALCHEMY_BINDS'] = {'rw': 'rw', 'r': 'r'}
db = SQLAlchemy(app)
db.Model_RW = db.make_declarative_base()
class A(db.Model):
__tablename__ = 'common'
__bind_key__ = 'rw'
class B(db.Model_RW):
__tablename__ = 'common'
__bind_key__ = 'r'
Getting the error TypeError: make_declarative_base() takes at least 2 arguments (1 given)
when I use that code.
How can I integrate this with a sessionmaker (currently i pass an engine to the session manager in the begging)?
engine = SessionManager.get_engine(uri)
session = sessionmaker(bind=engine)()
I tried to make the session object like this:
db = AutoRouteSQLAlchemy()
session = db.session
but i got:
AttributeError: 'scoped_session' object has no attribute '_autoflush'
later on in the code
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I forgot to mention you might also ping zzzeek, I know Mike is active on Twitter and StackOverflow. Probably on IRC too.