]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
document get_bind() overrides for async session
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 29 Nov 2021 22:00:21 +0000 (17:00 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 29 Nov 2021 22:07:46 +0000 (17:07 -0500)
Change-Id: Ifcc936a5861d49857d1f365416190cfbd0981aac
References: #7383

lib/sqlalchemy/ext/asyncio/session.py

index 89da4b4972fc3f9ea6d1995647f190554cc0b52e..9beba2ee0dc5f8b401cfffcd79734eaa08804590 100644 (file)
@@ -32,7 +32,6 @@ _STREAM_OPTIONS = util.immutabledict({"stream_results": True})
         "expire_all",
         "expunge",
         "expunge_all",
-        "get_bind",
         "is_modified",
         "in_transaction",
         "in_nested_transaction",
@@ -430,6 +429,84 @@ class AsyncSession(ReversibleProxy):
         else:
             return None
 
+    def get_bind(self, mapper=None, clause=None, bind=None, **kw):
+        """Return a "bind" to which the synchronous proxied :class:`_orm.Session`
+        is bound.
+
+        Unlike the :meth:`_orm.Session.get_bind` method, this method is
+        currently **not** used by this :class:`.AsyncSession` in any way
+        in order to resolve engines for requests.
+
+        .. note::
+
+            This method proxies directly to the :meth:`_orm.Session.get_bind`
+            method, however is currently **not** useful as an override target,
+            in contrast to that of the :meth:`_orm.Session.get_bind` method.
+            The example below illustrates how to implement custom
+            :meth:`_orm.Session.get_bind` schemes that work with
+            :class:`.AsyncSession` and :class:`.AsyncEngine`.
+
+        The pattern introduced at :ref:`session_custom_partitioning`
+        illustrates how to apply a custom bind-lookup scheme to a
+        :class:`_orm.Session` given a set of :class:`_engine.Engine` objects.
+        To apply a corresponding :meth:`_orm.Session.get_bind` implementation
+        for use with a :class:`.AsyncSession` and :class:`.AsyncEngine`
+        objects, continue to subclass :class:`_orm.Session` and apply it to
+        :class:`.AsyncSession` using
+        :paramref:`.AsyncSession.sync_session_class`. The inner method must
+        continue to return :class:`_engine.Engine` instances, which can be
+        acquired from a :class:`_asyncio.AsyncEngine` using the
+        :attr:`_asyncio.AsyncEngine.sync_engine` attribute::
+
+            # using example from "Custom Vertical Partitioning"
+
+
+            import random
+
+            from sqlalchemy.ext.asyncio import AsyncSession
+            from sqlalchemy.ext.asyncio import create_async_engine
+            from sqlalchemy.orm import Session, sessionmaker
+
+            # construct async engines w/ async drivers
+            engines = {
+                'leader':create_async_engine("sqlite+aiosqlite:///leader.db"),
+                'other':create_async_engine("sqlite+aiosqlite:///other.db"),
+                'follower1':create_async_engine("sqlite+aiosqlite:///follower1.db"),
+                'follower2':create_async_engine("sqlite+aiosqlite:///follower2.db"),
+            }
+
+            class RoutingSession(Session):
+                def get_bind(self, mapper=None, clause=None, **kw):
+                    # within get_bind(), return sync engines
+                    if mapper and issubclass(mapper.class_, MyOtherClass):
+                        return engines['other'].sync_engine
+                    elif self._flushing or isinstance(clause, (Update, Delete)):
+                        return engines['leader'].sync_engine
+                    else:
+                        return engines[
+                            random.choice(['follower1','follower2'])
+                        ].sync_engine
+
+            # apply to AsyncSession using sync_session_class
+            AsyncSessionMaker = sessionmaker(
+                class_=AsyncSession,
+                sync_session_class=RoutingSession
+            )
+
+        The :meth:`_orm.Session.get_bind` method is called in a non-asyncio,
+        implicitly non-blocking context in the same manner as ORM event hooks
+        and functions that are invoked via :meth:`.AsyncSession.run_sync`, so
+        routines that wish to run SQL commands inside of
+        :meth:`_orm.Session.get_bind` can continue to do so using
+        blocking-style code, which will be translated to implicitly async calls
+        at the point of invoking IO on the database drivers.
+
+        """  # noqa E501
+
+        return self.sync_session.get_bind(
+            mapper=mapper, clause=clause, bind=bind, **kw
+        )
+
     async def connection(self, **kw):
         r"""Return a :class:`_asyncio.AsyncConnection` object corresponding to
         this :class:`.Session` object's transactional state.