]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
add examples for multi metadata under __abstract__, custom vertical partitioning
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 11 Jan 2012 21:35:34 +0000 (16:35 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 11 Jan 2012 21:35:34 +0000 (16:35 -0500)
doc/build/orm/session.rst
lib/sqlalchemy/ext/declarative.py

index 1c1189507bbe3f30fc31f23a82550b6070c862fa..675b2136f14cccd14b8e5a371eee9da230a4f2a8 100644 (file)
@@ -1219,6 +1219,8 @@ autocommit is disabled, it is still useful in that it forces the :class:`.Sessio
 into a "pending rollback" state, as a failed flush cannot be resumed in mid-operation,
 where the end user still maintains the "scope" of the transaction overall.
 
+.. _session_twophase:
+
 Enabling Two-Phase Commit
 -------------------------
 
@@ -1507,8 +1509,8 @@ Contextual Session API
 Partitioning Strategies
 =======================
 
-Vertical Partitioning
----------------------
+Simple Vertical Partitioning
+----------------------------
 
 Vertical partitioning places different kinds of objects, or different tables,
 across multiple databases::
@@ -1523,6 +1525,61 @@ across multiple databases::
 
     session = Session()
 
+Above, operations against either class will make usage of the :class:`.Engine`
+linked to that class.   Upon a flush operation, similar rules take place
+to ensure each class is written to the right database.   
+
+The transactions among the multiple databases can optionally be coordinated
+via two phase commit, if the underlying backend supports it.  See
+:ref:`session_twophase` for an example.
+
+Custom Vertical Partitioning
+----------------------------
+
+More comprehensive rule-based class-level partitioning can be built by
+overriding the :meth:`.Session.get_bind` method.   Below we illustrate
+a custom :class:`.Session` which delivers the following rules:
+
+1. Flush operations are delivered to the engine named ``master``.
+
+2. Operations on objects that subclass ``MyOtherClass`` all 
+   occur on the ``other`` engine.
+
+3. Read operations for all other classes occur on a random
+   choice of the ``slave1`` or ``slave2`` database.
+
+::
+
+    engines = {
+        'master':create_engine("sqlite:///master.db"),
+        'other':create_engine("sqlite:///other.db"),
+        'slave1':create_engine("sqlite:///slave1.db"),
+        'slave2':create_engine("sqlite:///slave2.db"),
+    }
+
+    from sqlalchemy.orm import Session, sessionmaker
+    import random
+
+    class RoutingSession(Session):
+        def get_bind(self, mapper=None, clause=None):
+            if mapper and issubclass(mapper.class_, MyOtherClass):
+                return engines['other']
+            elif self._flushing:
+                return engines['master']
+            else:
+                return engines[
+                    random.choice(['slave1','slave2'])
+                ]
+
+The above :class:`.Session` class is plugged in using the ``class_``
+argument to :func:`.sessionmaker`::
+
+    Session = sessionmaker(class_=RoutingSession)
+
+This approach can be combined with multiple :class:`.MetaData` objects,
+using an approach such as that of using the declarative ``__abstract__`` 
+keyword, described at :ref:`declarative_abstract`.
+
 Horizontal Partitioning
 -----------------------
 
index 61ec5334390cbfd1c320e7c0f694712981abc4d7..bd655a721355761c05bda51a771f946ae0c743ff 100755 (executable)
@@ -906,6 +906,7 @@ has finished::
             ""
             # do something with mappings
 
+.. _declarative_abstract:
 
 ``__abstract__``
 ~~~~~~~~~~~~~~~~~~~
@@ -927,6 +928,26 @@ just from the special class::
 
     class MyMappedClass(SomeAbstractBase):
         ""
+        
+One possible use of ``__abstract__`` is to use a distinct :class:`.MetaData` for different
+bases::
+
+    Base = declarative_base()
+
+    class DefaultBase(Base):
+        __abstract__ = True
+        metadata = MetaData()
+
+    class OtherBase(Base):
+        __abstract__ = True
+        metadata = MetaData()
+
+Above, classes which inherit from ``DefaultBase`` will use one :class:`.MetaData` as the 
+registry of tables, and those which inherit from ``OtherBase`` will use a different one.  
+The tables themselves can then be created perhaps within distinct databases::
+
+    DefaultBase.metadata.create_all(some_engine)
+    OtherBase.metadata_create_all(some_other_engine)
 
 Class Constructor
 =================