1515from flask .signals import Namespace
1616from sqlalchemy import event , inspect , orm
1717from sqlalchemy .engine .url import make_url
18- from sqlalchemy .ext .declarative import DeclarativeMeta , declarative_base
1918from sqlalchemy .orm .exc import UnmappedClassError
2019from sqlalchemy .orm .session import Session as SessionBase
2120
22- from flask_sqlalchemy .model import Model
2321from ._compat import itervalues , string_types , xrange
2422from .model import DefaultMeta
23+ from .model import Model
2524from . import utils
2625
26+ try :
27+ from sqlalchemy .orm import declarative_base
28+ from sqlalchemy .orm import DeclarativeMeta
29+ except ImportError :
30+ # SQLAlchemy <= 1.3
31+ from sqlalchemy .ext .declarative import declarative_base
32+ from sqlalchemy .ext .declarative import DeclarativeMeta
33+
2734__version__ = "2.4.4"
2835
2936# the best timer function for the platform
4047before_models_committed = _signals .signal ('before-models-committed' )
4148
4249
50+ def _sa_url_set (url , ** kwargs ):
51+ try :
52+ url = url .set (** kwargs )
53+ except AttributeError :
54+ # SQLAlchemy <= 1.3
55+ for key , value in kwargs .items ():
56+ setattr (url , key , value )
57+
58+ return url
59+
60+
61+ def _sa_url_query_setdefault (url , ** kwargs ):
62+ query = dict (url .query )
63+
64+ for key , value in kwargs .items ():
65+ query .setdefault (key , value )
66+
67+ return _sa_url_set (url , query = query )
68+
69+
4370def _make_table (db ):
4471 def _make_table (* args , ** kwargs ):
4572 if len (args ) > 1 and isinstance (args [1 ], db .Column ):
@@ -552,7 +579,7 @@ def get_engine(self):
552579 return self ._engine
553580
554581 sa_url = make_url (uri )
555- options = self .get_options (sa_url , echo )
582+ sa_url , options = self .get_options (sa_url , echo )
556583 self ._engine = rv = self ._sa .create_engine (sa_url , options )
557584
558585 if _record_queries (self ._app ):
@@ -566,8 +593,9 @@ def get_engine(self):
566593 def get_options (self , sa_url , echo ):
567594 options = {}
568595
569- self ._sa .apply_pool_defaults (self ._app , options )
570- self ._sa .apply_driver_hacks (self ._app , sa_url , options )
596+ options = self ._sa .apply_pool_defaults (self ._app , options )
597+ sa_url , options = self ._sa .apply_driver_hacks (self ._app , sa_url , options )
598+
571599 if echo :
572600 options ['echo' ] = echo
573601
@@ -578,7 +606,7 @@ def get_options(self, sa_url, echo):
578606 # Give options set in SQLAlchemy.__init__() ultimate priority
579607 options .update (self ._sa ._engine_options )
580608
581- return options
609+ return sa_url , options
582610
583611
584612def get_state (app ):
@@ -861,6 +889,11 @@ def shutdown_session(response_or_exc):
861889 return response_or_exc
862890
863891 def apply_pool_defaults (self , app , options ):
892+ """
893+ .. versionchanged:: 2.5
894+ Returns the ``options`` dict, for consistency with
895+ :meth:`apply_driver_hacks`.
896+ """
864897 def _setdefault (optionkey , configkey ):
865898 value = app .config [configkey ]
866899 if value is not None :
@@ -869,6 +902,7 @@ def _setdefault(optionkey, configkey):
869902 _setdefault ('pool_timeout' , 'SQLALCHEMY_POOL_TIMEOUT' )
870903 _setdefault ('pool_recycle' , 'SQLALCHEMY_POOL_RECYCLE' )
871904 _setdefault ('max_overflow' , 'SQLALCHEMY_MAX_OVERFLOW' )
905+ return options
872906
873907 def apply_driver_hacks (self , app , sa_url , options ):
874908 """This method is called before engine creation and used to inject
@@ -879,9 +913,15 @@ def apply_driver_hacks(self, app, sa_url, options):
879913 The default implementation provides some saner defaults for things
880914 like pool sizes for MySQL and sqlite. Also it injects the setting of
881915 `SQLALCHEMY_NATIVE_UNICODE`.
916+
917+ .. versionchanged:: 2.5
918+ Returns ``(sa_url, options)``. SQLAlchemy 1.4 made the URL
919+ immutable, so any changes to it must now be passed back up
920+ to the original caller.
882921 """
883922 if sa_url .drivername .startswith ('mysql' ):
884- sa_url .query .setdefault ('charset' , 'utf8' )
923+ sa_url = _sa_url_query_setdefault (sa_url , charset = "utf8" )
924+
885925 if sa_url .drivername != 'mysql+gaerdbms' :
886926 options .setdefault ('pool_size' , 10 )
887927 options .setdefault ('pool_recycle' , 7200 )
@@ -911,7 +951,9 @@ def apply_driver_hacks(self, app, sa_url, options):
911951
912952 # if it's not an in memory database we make the path absolute.
913953 if not detected_in_memory :
914- sa_url .database = os .path .join (app .root_path , sa_url .database )
954+ sa_url = _sa_url_set (
955+ sa_url , database = os .path .join (app .root_path , sa_url .database )
956+ )
915957
916958 unu = app .config ['SQLALCHEMY_NATIVE_UNICODE' ]
917959 if unu is None :
@@ -932,6 +974,8 @@ def apply_driver_hacks(self, app, sa_url, options):
932974 DeprecationWarning
933975 )
934976
977+ return sa_url , options
978+
935979 @property
936980 def engine (self ):
937981 """Gives access to the engine. If the database configuration is bound
0 commit comments