Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ A list of configuration keys currently understood by the extension:
different default timeout value. For more
information about timeouts see
:ref:`timeouts`.
``SQLALCHEMY_POOL_PRE_PING`` Native "pessimistic disconnection" handling
to the Pool object. This emits a simple
statement, typically "SELECT 1",
to test the connection for liveness.
If the existing connection is no longer able
to respond to commands, the connection is
transparently recycled, and all other
connections made prior to the current
timestamp are invalidated.
``SQLALCHEMY_MAX_OVERFLOW`` Controls the number of connections that
can be created after the pool reached
its maximum size. When those additional
Expand Down
21 changes: 16 additions & 5 deletions flask_sqlalchemy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@
from sqlalchemy import event, inspect, orm
from sqlalchemy.engine.url import make_url
from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base
from sqlalchemy.schema import MetaData
from sqlalchemy.orm.exc import UnmappedClassError
from sqlalchemy.orm.session import Session as SessionBase

from flask_sqlalchemy.model import Model
from ._compat import itervalues, string_types, to_str, xrange
from .model import DefaultMeta

__version__ = '2.3.2'
__version__ = '2.3.3-dev'

# the best timer function for the platform
if sys.platform == 'win32':
Expand All @@ -47,10 +48,10 @@

def _make_table(db):
def _make_table(*args, **kwargs):
if len(args) > 1 and isinstance(args[1], db.Column):
args = (args[0], db.metadata) + args[1:]
info = kwargs.pop('info', None) or {}
info.setdefault('bind_key', None)
if len(args) > 1 and isinstance(args[1], db.Column):
args = (args[0], db.get_metadata(bind=info['bind_key'])) + args[1:]
kwargs['info'] = info
return sqlalchemy.Table(*args, **kwargs)
return _make_table
Expand Down Expand Up @@ -678,6 +679,7 @@ def __init__(self, app=None, use_native_unicode=True, session_options=None,
self.use_native_unicode = use_native_unicode
self.Query = query_class
self.session = self.create_scoped_session(session_options)
model_class._metadata = {}
self.Model = self.make_declarative_base(model_class, metadata)
self._engine_lock = Lock()
self.app = app
Expand All @@ -692,6 +694,13 @@ def metadata(self):

return self.Model.metadata

def get_metadata(self, bind=None):
if not bind:
return self.metadata
if bind not in self.Model._metadata:
self.Model._metadata[bind] = MetaData()
return self.Model._metadata.get(bind)

def create_scoped_session(self, options=None):
"""Create a :class:`~sqlalchemy.orm.scoping.scoped_session`
on the factory from :meth:`create_session`.
Expand Down Expand Up @@ -787,6 +796,7 @@ def init_app(self, app):
app.config.setdefault('SQLALCHEMY_POOL_SIZE', None)
app.config.setdefault('SQLALCHEMY_POOL_TIMEOUT', None)
app.config.setdefault('SQLALCHEMY_POOL_RECYCLE', None)
app.config.setdefault('SQLALCHEMY_POOL_PRE_PING', False)
app.config.setdefault('SQLALCHEMY_MAX_OVERFLOW', None)
app.config.setdefault('SQLALCHEMY_COMMIT_ON_TEARDOWN', False)
track_modifications = app.config.setdefault(
Expand Down Expand Up @@ -819,6 +829,7 @@ def _setdefault(optionkey, configkey):
_setdefault('pool_size', 'SQLALCHEMY_POOL_SIZE')
_setdefault('pool_timeout', 'SQLALCHEMY_POOL_TIMEOUT')
_setdefault('pool_recycle', 'SQLALCHEMY_POOL_RECYCLE')
_setdefault('pool_pre_ping', 'SQLALCHEMY_POOL_PRE_PING')
_setdefault('max_overflow', 'SQLALCHEMY_MAX_OVERFLOW')

def apply_driver_hacks(self, app, info, options):
Expand Down Expand Up @@ -921,7 +932,7 @@ def get_app(self, reference_app=None):
def get_tables_for_bind(self, bind=None):
"""Returns a list of all tables relevant for a bind."""
result = []
for table in itervalues(self.Model.metadata.tables):
for table in itervalues(self.get_metadata(bind=bind).tables):
if table.info.get('bind_key') == bind:
result.append(table)
return result
Expand Down Expand Up @@ -955,7 +966,7 @@ def _execute_for_all_tables(self, app, bind, operation, skip_tables=False):
if not skip_tables:
tables = self.get_tables_for_bind(bind)
extra['tables'] = tables
op = getattr(self.Model.metadata, operation)
op = getattr(self.get_metadata(bind=bind), operation)
op(bind=self.get_engine(app, bind), **extra)

def create_all(self, bind='__all__', app=None):
Expand Down
7 changes: 6 additions & 1 deletion flask_sqlalchemy/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sqlalchemy as sa
from sqlalchemy import inspect
from sqlalchemy.ext.declarative import DeclarativeMeta, declared_attr
from sqlalchemy.schema import _get_table_key
from sqlalchemy.schema import _get_table_key, MetaData

from ._compat import to_str

Expand Down Expand Up @@ -118,6 +118,11 @@ def __init__(cls, name, bases, d):
or getattr(cls, '__bind_key__', None)
)

if bind_key:
if bind_key not in cls._metadata:
cls._metadata[bind_key] = MetaData()
cls.metadata = cls._metadata[bind_key]

super(BindMetaMixin, cls).__init__(name, bases, d)

if bind_key is not None and hasattr(cls, '__table__'):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
platforms='any',
install_requires=[
'Flask>=0.10',
'SQLAlchemy>=0.8.0'
'SQLAlchemy>=1.2.0'
],
test_suite='test_sqlalchemy.suite',
classifiers=[
Expand Down