DB-API X
========
``dbapix`` is a unification of, and extension to, several DB-API 2.0 drivers.
We currently wrap:
- ``"postgres"`` via `psycopg2 `_
- ``"sqlite"`` via `sqlite3 `_
- ``"mysql"`` via `PyMySQL `_ or `MySQL-Python `_
The first goal is to provide a :class:`pool <.Engine>` of normalized
DB-API 2.0 :class:`connections <.Connection>` and :class:`cursors <.Cursor>`,
and for them to have the same Python API and semantics regardless of the driver.
The second goal is to provide some common niceties:
- f-string-like parameter binding;
- high-level functions, e.g. ``insert``, ``update``, etc.;
- dict-like rows;
- interaction with 3rd-party libraries, e.g. Pandas;
- automatic SSH tunnels;
- a registry of connections.
Examples
========
Basics
------
Create a connection pool, and "borrow" a connection from it (which will be
automatically returned when exiting the context)::
from dbapix import create_engine
# Create an engine with the name of the driver. Here we're connecting to
# a PostgreSQL database via the `psycopg2` package.
engine = create_engine('postgres',
host='localhost',
database='example',
)
# Context managers provide reliable resource management; the connection
# will be returned to the pool when exiting this context.
with engine.connect() as con:
foo_id = 123
# If not provided explicitly, parameters are pulled
# from the calling scope for an f-string-like experience
# but where Bobby Tables won't cause trouble.
cur = con.cursor()
cur.execute('''SELECT bar FROM foo WHERE id = {foo_id}''')
for row in cur:
# Values are accessible by index or name.
print(row['bar'])
You can also manually manage connections, and directly turn results into
Pandas dataframes::
from dbapix import create_engine
# Lets use SQLite this time, via the `sqlite3` driver.
engine = create_engine('sqlite', 'mydatabase.db')
# Take ownership of a connection. This is now our responsibility to
# either close, or return to the pool via `engine.put_connection(con)`.
con = engine.get_connection()
# Connections have an `execute` method which creates a cursor for us.
cur = con.execute('''
SELECT foo, sum(bar) AS bar
FROM myawesometable
GROUP BY baz
''')
# Turn the result into a Pandas DataFrame!
df = cur.as_dataframe()
SSH Tunnels
-----------
If you need an SSH tunnel to connect, you can give a set of kwargs to be passed
to a `SSHTunnelForwarder `_ for the Postgres
and MySQL engines::
from dbapix import create_engine
engine = create_engine('postgres',
database='only_on_remote',
tunnel=dict(
host='database.example.com',
),
)
# The tunnel will be created at the first connection.
engine.close() # Shut it down explicitly.
See :class:`.SocketEngine` for more.
Registries
----------
If you have multiple databases that you're shifting bettween, e.g. staging
and production, we have a :class:`.Registry` that you can use to contain
the connection details.
I might make a common file with that registry, e.g. ``mydbs.py``::
from dbapix.registry import Registry
registry = Registry()
registry.register('staging', 'postgres',
host='stage.example.com',
database='myapp-stage',
user='devuser',
password='hunter2',
)
registry.register('production', 'postgres',
# Need an SSH tunnel to production!
tunnel=dict(
host='gateway.prod.example.com', # The SSH host.
remote_bind_host='database.prod.example.com' # The database host.
ssh_pkey=os.path.expanduser('~/.ssh/id_rsa'), # Key isn't pulled in by default.
)
database='myapp',
user='appuser',
password='HuNt3r2',
)
# "Export" this registry method as the API of my module.
create_engine = registry.create_engine
Then in my scripts, e.g. ``awesome_data_science.py``::
import mydbs
engine = mydbs.create_engine('staging')
with engine.connect() as con:
cur = con.execute('select 1 as foo')
df = cur.as_dataframe()
# Do some science!
API Reference
=============
.. toctree::
api/core
api/engine
api/connection
api/cursor
api/row
api/query
api/params
api/registry
---
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`