database - Public database interface

The database interface class database.DBInterface requires a valid connection string (or database URL) on instantiation. For convenience, the connection string templates and associated documentation can be found in the Using the Library --> Connection Strings section of this documentation.

Purpose:

This module provides the library's primary entry-point for accessing the database methods and attributes.

Platform:

Linux/Windows | Python 3.10+

Developer:

J Berendt

Email:

support@s3dev.uk

Comments:

n/a

Example:

For class-specific usage examples, please refer to the docstring for the following classes:

class database.DBInterface(connstr: str, *args, **kwargs)[source]

This class holds the methods and properties which are used across all databases. This class is the primary entry-point for the database interface.

Database-specific functionality is provided by this class' __new__() method, which returns the appropriate instance of the lower-level database-specific class, depending on the connection string provided. Or, more specifically, the sqlalchemy.Engine object created from the provided connection string.

Note

Due to the way this class is created - for specific design reasons - the inheriting class' __init__ method will not be called. Therefore, specialisation is not as simple as inheritance and calling the super() function.

See the examples below for a use-case in how to specialise this class with your own methods.

Parameters:

connstr (str) -- The database-specific SQLAlchemy connection string.

Example Use:

This low-level generalised class is designed to be instantiated by local program or database module, as:

>>> from dblib.database import DBInterface

>>> dbi = DBInterface(connstr=('mysql+mysqlconnector://'
                               '<user>:<pwd>@<host>:<port>/'
                               '<db_name>'))
>>> dbi.engine
Engine(mysql+mysqlconnector://<user>:***@<host>:<port>/db_name)

For example, the dbi instance can be used to execute a query, as:

>>> result = dbi.execute_query('SELECT COUNT(*) FROM mytable');
>>> result
[(14,)]

Additionally, the dbi.engine object can be supplied to the pandas.read_sql() function's con parameter, as the database connection object, as:

>>> import pandas as pd

>>> sql = 'select count(*) from mytable'
>>> df = pd.read_sql(sql, con=dbi.engine)

>>> df
    count(*)
 0        14
Subclass Specialisation:

To specialise the subclass, a bit of 'pseudo-inheritance' is required due to the way the DBInterface class is created. A 'standard inheritance' with a call to super() does not work, as the subclass' __init__ method is not called. Therefore, the subclass must add the parent's attributes into its class, manually.

This can be done as follows:

from dblib.database import DBInterface

class MyDBI:

    def __init__(self, connstr: str):
        #
        # Pseudo-inherit the DBInterface class by 'copying'
        # the attributes into this subclass.
        #
        # There are many ways to do this. This is the most
        # general, as functions, methods and properties are
        # captured.
        #
        self._dbi = DBInterface(connstr=connstr)
        fns = [fn for fn in dir(self._dbi) if not fn.startswith('__')]
        for fn in fns:
            setattr(self, fn, self._dbi.__getattribute__(fn))

    @property
    def spam(self):
        return 'spam'

    # Continuation of class definition ...

>>> db = MyDBI(connstr=('mysql+mysqlconnector://'
                        '<user>:<pwd>@<host>:<port>/'
                        '<db_name>'))
# List the database interface class' attributes.
# Notice that 'spam' is included in the list, along with the
# methods from the :class:`DBInterface` class, thus
# *simulating* inheritance.
>>> dir(db)
['_PREFIX',
 '__class__',
 '__delattr__',
 ...,
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_connstr',
 '_create_engine',
 '_dbi',
 '_engine',
 '_report_sa_error',
 '_result_to_df__cursor',
 '_result_to_df__stored',
 'call_procedure',
 'call_procedure_update',
 'call_procedure_update_many',
 'call_procedure_update_raw',
 'database_name',
 'engine',
 'execute_query',
 'spam',           # <---
 'table_exists']]
_SUPPORTED_DBS = ['mysql', 'oracle', 'sqlite']
static __new__(cls, connstr: str, *args, **kwargs)[source]

Provide a database interface based on the connection string.

Using the provided connection string, a sqlalchemy.engine.base.Engine object is created. Using the .name attribute, an instance of the associated database interface class is returned.

For example, if the .name attribute is 'mysql', an instance of the _dbi_mysql._DBIMySQL private interface class is returned. Likewise, if the .name attribute is 'oracle', an instance of the _dbi_oracle._DBIOracle private interface class is returned, etc.

Parameters:

connstr (str) -- The SQLAlchemy-syle connection string, from which the sqlalchemy.engine.base.Engine is created for the database interface instance.

static _create_engine__internal_only(connstr: str) str[source]

Create a database engine using the provided connection string.

Warning

This method is not to be called independently as:

  • The engine itself is not returned.

  • The connect is disposed immediately after creation.

  • The pool_* objects are not set.

The engine created here is only meant to providing database-class routing for this class' __new__() method.

Parameters:

connstr (str) -- The SQLAlchemy connection string.

Returns:

The value of the .name attribute from the database engine.

Return type:

str