Advanced topics¶
Multiple databases¶
Sometimes you may need to talk to multiple databases. This requires you to do two things: define the data model for each database, and configure the connection for each database. pyramid_sqlalchemy has support for multiple databases build-in.
The first thing you will need to do is specify for each ORM class or table which
connection it is associated with. Each connection is identified by a name. If
you do not specify a name default
is used instead.
Caution
It is strongly recommended to enable two-phased transactions when using multiple databases. Without this there is a risk that the databases can be out of sync.
Here is an example that defines a user table in the default database, and a transaction table in a data warehouse:
1 2 3 4 5 6 7 8 | from pyramid_sqlalchemy import orm_base
class User(orm_base()):
__tablename__ = 'user'
class Transaction(orm_base('warehouse')):
__tablename__ = 'transaction'
|
You can now see why orm_base
is a function: you can pass it the connection name
and it will do the necessary linking.
The same approach works if you want to use the metadata instance directly, without using the ORM:
1 2 3 4 5 6 7 | from pyramid_sqlalchemy import get_metadata
from sqlalchemy import Column, Table, Integer, String
transaction = Table('transaction', get_metadata('warehouse),
Column('tx_id', Integer, primary_key=True),
...
)
|
To connect to a database you must call bind_connection
once for each connection:
1 2 3 4 5 6 7 | from sqlalchemy import create_engine
from pyramid_sqlalchemy import bind_connection
# Use a local PostgreSQL database for each default data
bind_connection(create_engine('postgresql:///myapp'))
# And connect to a remote instance for our data warehouse
bind_connection(create_engine('postgresql://data-warehouse/myapp'), 'warehouse')
|
When querying or updating a database you can request a session for a specific connection. Building on our example, this is how you can retrieve the latest transaction from the data warehouse:
1 2 3 4 5 6 7 | from pyramid_sqlalchemy import get_sql_session
sql_session = get_sql_session('warehouse')
latest_tx = sql_session.query(Transaction)\
.order_by(Transaction.timestamp)\
.limit(1)\
.first()
|
Since it can be tedious to always pass the name of the connection to
get_sql_session
you can also use get_sql_query
, which is smart
enough to figure out which session it needs to use.
1 2 3 4 5 6 | from pyramid_sqlalchemy import get_sql_query
latest_tx = get_sql_query(Transaction)\
.order_by(Transaction.timestamp)\
.limit(1)\
.first()
|
When writing tests you will need to define an extra fixture for extra
connections you create. This is easily done via the
pyramid_sqlalchemy.fixtures.sql_session_fixture()
helper.
1 2 3 4 5 6 7 8 | import pytest
from pyramid_sqlalchemy.fixtures import sql_session_fixture
@pytest.yield_fixture
def warehouse_sql_session():
"""A SQLAlchemy session for the warehouse database.
"""
yield from sql_session_fixture('warehouse', 'sqlite:///')
|
If you need to support Python versions before 3.3 you can not use yield from
and need to use this more verbose variant:
1 2 3 4 5 6 7 8 9 | import pytest
from pyramid_sqlalchemy.fixtures import sql_session_fixture
@pytest.yield_fixture
def warehouse_sql_session():
"""A SQLAlchemy session for the warehouse database.
"""
for f in sql_session_fixture('warehouse', 'sqlite:///'):
yield f
|
Two-phase transactions¶
If your application uses multiple databases or other transaction-aware systems
such as repoze.filesafe, AcidFS, pyramid_mailer or ZODB
you need to two-phase commits to coordinate
transactions. You can enable these using the enable_sql_two_phase_commit
configuration directive.
1 2 3 | from pyramid_sqlalchemy import configure_all_connections
configure_all_connections(twophase=True)
|
For Pyramid application you can also do this through the configurator instance:
1 2 3 4 | def main(global_config, **settings):
config = Configurator(settings=settings)
config.include('pyramid_sqlalchemy')
config.enable_sql_two_phase_commit()
|
Warning
Please note that this is not supported for all SQL servers. PostgreSQL is the only server where this is guaranteed to work. SQL Server does support two -phase transactions, but the Python driver support for it is unusable.