Явная настройка Master-Master DB с помощью Flask и SQLAlchemy, желательно с Flask-SQLAlchemy.

Я хочу использовать явную настройку БД master-master вместе с Flask и SQLAlchemy, надеюсь, это поддерживается 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

Спасибо за помощь.


person Oded    schedule 29.10.2013    source источник


Ответы (1)


Код ниже - это то, что я получил, чтобы иметь эту функциональность.

Несколько заметок:

  • Я изменил get_table_for_bind, чтобы привязать все таблицы без явного __bind_key__ ко всем привязкам. Это сделано для того, чтобы иметь возможность вызывать db.create_all() или db.drop_all() и создавать/удалять таблицы во всех БД. Чтобы это работало и не нарушало выбор БД по умолчанию, когда не указывалась конкретная привязка, get_binds было изменено, чтобы снова сопоставить привязку None после исходной реализации, чтобы переопределить сопоставление Таблица-> Привязка.
  • Если вы не укажете using_bind, все должно работать с БД по умолчанию.
  • Сопоставленные объекты SQLAlchemy сохраняют ссылку на сеанс и состояние, поэтому вы не можете добавить один и тот же объект к двум БД. Я сделал копию объекта перед его добавлением, чтобы сохранить его в двух БД. Не уверен, что есть лучший способ сделать это.
  • Я не проверял это полностью, и это может нарушить некоторые другие функции, которые я не использую или о которых не знаю.

flask-sqlalchemy переопределяет:

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()
person Oded    schedule 02.12.2013