database - Public database interface — dbilib - v0.5.0 Documentation

# 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:

  > - `DBInterface`

class DBInterface(connstr: str, *args, **kwargs)

  Bases: `object`

  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 dbilib.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 dbilib.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 = ['mssql', 'mysql', 'oracle', 'sqlite']
  static __new__(cls, connstr: str, *args, **kwargs)

    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

    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