oslo.db/doc/source/usage.rst
Roman Podoliaka 8a5fbb723f Make it possible to use enginefacade decorators with class methods
The decorator form can now be used with bound methods after a fix
for the related bug was merged. Mention this in docs and release
notes, so that people are aware of it.

A new test is added to make sure this also works with class methods
(as well as to check that the proposed decorator applying order
is actually correct).

Related-Bug: #1520195

Change-Id: Ifea08114d6d89de9d67fcae397eb94c0afc4d339
2016-06-27 17:22:01 +03:00

4.5 KiB

Usage

To use oslo.db in a project:

Session Handling

Session handling is achieved using the oslo_db.sqlalchemy.enginefacade system. This module presents a function decorator as well as a context manager approach to delivering .Session as well as .Connection objects to a function or block.

Both calling styles require the use of a context object. This object may be of any class, though when used with the decorator form, requires special instrumentation.

The context manager form is as follows:

from oslo_db.sqlalchemy import enginefacade


class MyContext(object):
    "User-defined context class."


def some_reader_api_function(context):
    with enginefacade.reader.using(context) as session:
        return session.query(SomeClass).all()


def some_writer_api_function(context, x, y):
    with enginefacade.writer.using(context) as session:
        session.add(SomeClass(x, y))


def run_some_database_calls():
    context = MyContext()

    results = some_reader_api_function(context)
    some_writer_api_function(context, 5, 10)

The decorator form accesses attributes off the user-defined context directly; the context must be decorated with the oslo_db.sqlalchemy.enginefacade.transaction_context_provider decorator. Each function must receive the context argument:

from oslo_db.sqlalchemy import enginefacade

@enginefacade.transaction_context_provider
class MyContext(object):
    "User-defined context class."

@enginefacade.reader
def some_reader_api_function(context):
    return context.session.query(SomeClass).all()


@enginefacade.writer
def some_writer_api_function(context, x, y):
    context.session.add(SomeClass(x, y))


def run_some_database_calls():
    context = MyContext()

    results = some_reader_api_function(context)
    some_writer_api_function(context, 5, 10)

Note

The context.session and context.connection attributes must be accessed within the scope of an appropriate writer/reader block (either the decorator or contextmanager approach). An AttributeError is raised otherwise.

The decorator form can also be used with class and instance methods which implicitly receive the first positional argument:

class DatabaseAccessLayer(object):

    @classmethod
    @enginefacade.reader
    def some_reader_api_function(cls, context):
        return context.session.query(SomeClass).all()

    @enginefacade.writer
    def some_writer_api_function(self, context, x, y):
        context.session.add(SomeClass(x, y))

Note

Note that enginefacade decorators must be applied before classmethod, otherwise you will get a TypeError at import time (as enginefacade will try to use inspect.getargspec() on a descriptor, not on a bound method, please refer to the Data Model section of the Python Language Reference for details).

The scope of transaction and connectivity for both approaches is managed transparently. The configuration for the connection comes from the standard oslo_config.cfg.CONF collection. Additional configurations can be established for the enginefacade using the oslo_db.sqlalchemy.enginefacade.configure function, before any use of the database begins:

from oslo_db.sqlalchemy import enginefacade

enginefacade.configure(
    sqlite_fk=True,
    max_retries=5,
    mysql_sql_mode='ANSI'
)

Base class for models usage

from oslo_db.sqlalchemy import models


class ProjectSomething(models.TimestampMixin,
                       models.ModelBase):
    id = Column(Integer, primary_key=True)
    ...

DB API backend support

from oslo_config import cfg
from oslo_db import api as db_api


_BACKEND_MAPPING = {'sqlalchemy': 'project.db.sqlalchemy.api'}

IMPL = db_api.DBAPI.from_config(cfg.CONF, backend_mapping=_BACKEND_MAPPING)

def get_engine():
    return IMPL.get_engine()

def get_session():
    return IMPL.get_session()

# DB-API method
def do_something(somethind_id):
    return IMPL.do_something(somethind_id)

DB migration extensions

Available extensions for oslo_db.migration.

oslo_db.sqlalchemy.migration