I spent a lot of time finding the answer to this question. This is much easier to do today than when I initially asked this question, but it is still not entirely simple.
For those who decide to ensure their own security, I recommend the following excellent presentation of common design patterns that use a flask, but which avoid unnecessary dependencies such as flash protection: https://exploreflask.com/users.html
UPDATE: For anyone interested, the patch has been in work for some time related to this. It has not been released yet, but you can check its progress here: https://github.com/mitsuhiko/flask-sqlalchemy/pull/250#issuecomment-77504337
UPDATE: I took the code from the above patch and created a local override for the SQLAlchemy object, which allows you to register an external database. I think this is the best option available until the FSA starts adding this officially. Here is the code for this class for anyone interested. Tested using Flask-SqlAlchemy 2.2
Patch in register_external_base:
import flask_sqlalchemy '''Created by Isaac Martin 2017. Licensed insofar as it can be according to the standard terms of the MIT license: https://en.wikipedia.org/wiki/MIT_License. The author accepts no liability for consequences resulting from the use of this software. ''' class SQLAlchemy(flask_sqlalchemy.SQLAlchemy): def __init__(self, app=None, use_native_unicode=True, session_options=None, metadata=None, query_class=flask_sqlalchemy.BaseQuery, model_class=flask_sqlalchemy.Model): self.use_native_unicode = use_native_unicode self.Query = query_class self.session = self.create_scoped_session(session_options) self.Model = self.make_declarative_base(model_class, metadata) self._engine_lock = flask_sqlalchemy.Lock() self.app = app flask_sqlalchemy._include_sqlalchemy(self, query_class) self.external_bases = [] if app is not None: self.init_app(app) def get_tables_for_bind(self, bind=None): """Returns a list of all tables relevant for a bind.""" result = [] for Base in self.bases: for table in flask_sqlalchemy.itervalues(Base.metadata.tables): if table.info.get('bind_key') == bind: result.append(table) return result def get_binds(self, app=None): """Returns a dictionary with a table->engine mapping. This is suitable for use of sessionmaker(binds=db.get_binds(app)). """ app = self.get_app(app) binds = [None] + list(app.config.get('SQLALCHEMY_BINDS') or ()) retval = {} for bind in binds: engine = self.get_engine(app, bind) tables = self.get_tables_for_bind(bind) retval.update(dict((table, engine) for table in tables)) return retval @property def bases(self): return [self.Model] + self.external_bases def register_base(self, Base): """Register an external raw SQLAlchemy declarative base. Allows usage of the base with our session management and adds convenience query property using self.Query by default.""" self.external_bases.append(Base) for c in Base._decl_class_registry.values(): if isinstance(c, type): if not hasattr(c, 'query') and not hasattr(c, 'query_class'): c.query_class = self.Query if not hasattr(c, 'query'): c.query = flask_sqlalchemy._QueryProperty(self)
Used like this:
app = Flask(__name__) db = SQLAlchemy(app) db.register_base(base)