【问题标题】:How to separate Master Slave (DB read / writes) in Flask Sqlalchemy如何在 Flask Sqlalchemy 中分离主从(数据库读/写)
【发布时间】:2019-12-30 06:45:26
【问题描述】:

我正在尝试通过 Flask Sqlalchemy 分离读取和写入数据库操作。我正在使用绑定连接到 mysql 数据库。我想在主机中执行写操作并从从机读取。似乎没有内置的方法来处理这个问题。

我是 python 新手,我很惊讶像这样的急需功能还没有预先内置到 flask-sqlalchemy 中。任何帮助表示赞赏。谢谢

【问题讨论】:

标签: python sqlalchemy flask master-slave flask-sqlalchemy


【解决方案1】:

没有官方支持,但是可以自定义Flask-SQLalchemy session 使用主从连接

from functools import partial

from sqlalchemy import orm
from flask import current_app
from flask_sqlalchemy import SQLAlchemy, get_state


class RoutingSession(orm.Session):
    def __init__(self, db, autocommit=False, autoflush=True, **options):
        self.app = db.get_app()
        self.db = db
        self._bind_name = None
        orm.Session.__init__(
            self, autocommit=autocommit, autoflush=autoflush,
            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:
            current_app.logger.info(
                'cant get configuration. default bind. Error:' + err)
            return orm.Session.get_bind(self, mapper, clause)

        # If there are no binds configured, use default SQLALCHEMY_DATABASE_URI
        if not state or not self.app.config['SQLALCHEMY_BINDS']:
            return orm.Session.get_bind(self, mapper, clause)

        # if want to user exact bind
        if self._bind_name:
            return state.db.get_engine(self.app, bind=self._bind_name)
        else:
            # if no bind is used connect to default
            return orm.Session.get_bind(self, mapper, clause)

    def using_bind(self, name):
        bind_session = RoutingSession(self.db)
        vars(bind_session).update(vars(self))
        bind_session._bind_name = name
        return bind_session


class RouteSQLAlchemy(SQLAlchemy):
    def __init__(self, *args, **kwargs):
        SQLAlchemy.__init__(self, *args, **kwargs)
        self.session.using_bind = lambda s: self.session().using_bind(s)

    def create_scoped_session(self, options=None):
        if options is None:
            options = {}
        scopefunc = options.pop('scopefunc', None)
        return orm.scoped_session(
            partial(RoutingSession, self, **options),
            scopefunc=scopefunc,
        )

默认会话为master,当你想从slave中选择时,你可以直接调用它,这里是例子:

在您的应用中:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql:///master'
app.config['SQLALCHEMY_BINDS'] = {
    'slave': 'postgresql:///slave'
}

db = RouteSQLAlchemy(app)

从母版中选择

session.query(User).filter_by(id=1).first() 

从奴隶中选择

session.using_bind('slave').query(User).filter_by(id=1).first() 

【讨论】:

  • 真的很好的方法家伙!只有一个建议,我会从RoutingSession.__init__() 方法中删除autocommit=False, autoflush=False,因为它们定义了与orm.Session.__init__() 方法不同的默认值(至少在最新的FlaskAlchemy 版本中)。我花了一些时间才弄清楚为什么我的应用程序没有按预期工作,这就是原因。
  • @Ander 谢谢你的建议,修正autoflush 默认值
【解决方案2】:

【讨论】:

  • 这是简单的绑定。我无法提到 flash-sqlalchemy ORM 使用单独的绑定进行读取和写入。我可能不得不为 Model 类编写一个包装器并在那里处理它。
  • @Nands 你解决了吗?面临同样的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-24
  • 1970-01-01
  • 1970-01-01
  • 2021-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多