Assorted utility functions to support working with SQLAlchemy.
Project description
Assorted utility functions to support working with SQLAlchemy.
Latest release 20210420:
- ORM: drop .Session from docstring, no longer used.
- Rename ORM.sessionmaker to ORM._sessionmaker, not for public use.
- ORM: replace session with arranged_session, which allocates a session in conformance with ORM.serial_sessions (serial sessions are used with SQLite).
- Drop @ORM.auto_session and @ORM.orm_method decorators, no longer used.
- SQLAState.new_session: use orm.arranged_session(), use begin_nested(); SQLAState.auto_session: use begin_nested().
Function auto_session(function)
Decorator to run a function in a session if one is not presupplied.
The function function
runs within a transaction,
nested if the session already exists.
See with_session
for details.
Class BasicTableMixin
Useful methods for most tables.
Method BasicTableMixin.lookup(*, session, **criteria)
Return iterable of row entities matching criteria
.
Method BasicTableMixin.lookup1(*, session, **criteria)
Return the row entity matching criteria
, or None
if no match.
Function find_json_field(column_value, field_name, *, infill=False)
Descend a JSONable Python object column_value
to field_name
.
Return column_value
(possibly infilled), final_field
, final_field_name
.
This supports database row columns which are JSON columns.
Parameters:
column_value
: the original value of the columnfield_name
: the field within the column to locateinfill
: optional keyword parameter, defaultFalse
. If true,column_value
and its innards will be filled in asdict
s to allow deferencing thefield_name
.
The field_name
is a str
consisting of a period ('.'
) separated sequence of field parts.
Each field part becomes a key to index the column mapping.
These keys are split into the leading field parts
and the final field part,
which is returned as final_field_name
above.
The final_field
return value above
is the mapping within which final_field_value
may lie
and where final_field_value
may be set.
Note: it may not be present.
If a leading key is missing and infill
is true
the corresponding part of the column_value
is set to an empty dictionary
in order to allow deferencing the leading key.
This includes the case when column_value
itself is None
,
which is why the column_value
is part of the return.
If a leading key is missing and infill
is false
this function will raise a KeyError
for the portion of the field_name
which failed.
Examples:
>>> find_json_field({'a':{'b':{}}}, 'a.b')
({'a': {'b': {}}}, {'b': {}}, 'b')
>>> find_json_field({'a':{}}, 'a.b')
({'a': {}}, {}, 'b')
>>> find_json_field({'a':{'b':{}}}, 'a.b.c.d')
Traceback (most recent call last):
...
KeyError: 'a.b.c'
>>> find_json_field({'a':{'b':{}}}, 'a.b.c.d', infill=True)
({'a': {'b': {'c': {}}}}, {}, 'd')
>>> find_json_field(None, 'a.b.c.d')
Traceback (most recent call last):
...
KeyError: 'a'
>>> find_json_field(None, 'a.b.c.d', infill=True)
({'a': {'b': {'c': {}}}}, {}, 'd')
Function get_json_field(column_value, field_name, *, default=None)
Return the value of field_name
from column_value
or a defaault if the field is not present.
Parameters:
column_value
: the original value of the columnfield_name
: the field within the column to locatedefault
: default value to return if the field is not present, default:None
Examples:
>>> get_json_field({'a': 1}, 'a')
1
>>> get_json_field({'b': 1}, 'a')
>>> get_json_field({'a': {}}, 'a.b')
>>> get_json_field({'a': {'b': 2}}, 'a.b')
2
Class HasIdMixin
Include an "id" Column
as the primary key.
Function json_column(*da, **dkw)
Class decorator to declare a virtual column name on a table where the value resides inside a JSON column of the table.
Parameters:
cls
: the class to annotateattr
: the virtual column name to present as a row attributejson_field_name
: the field within the JSON column used to store this value, default the same asattr
json_column_name
: the name of the associated JSON column, default'info'
default
: the default value returned by the getter if the field is not present, defaultNone
Example use:
Base = declarative_base()
...
@json_column('virtual_name', 'json.field.name')
class TableClass(Base):
...
This annotates the class with a .virtual_name
property
which can be accessed or set,
accessing or modifying the associated JSON column
(in this instance, the column info
,
accessing info['json']['field']['name']
).
Function log_level(*da, **dkw)
Decorator for functions which wraps calls to the function in a context manager, optionally supplying the context as the first argument to the called function.
Class ORM(cs.resources.MultiOpenMixin)
A convenience base class for an ORM class.
This defines a .Base
attribute which is a new DeclarativeBase
and provides various Session related convenience methods.
It is also a MultiOpenMixin
subclass
supporting nested open/close sequences and use as a context manager.
Subclasses must define the following:
.startup
and.shutdown
methods to support theMultiOpenMixin
, even if these justpass
Method ORM.__init__(self, *a, **kw)
Initialise the ORM.
If serial_sessions
is true (default False
)
then allocate a lock to serialise session allocation.
This might be chosen with SQL backends which do not support
concurrent sessions such as SQLite.
In the case of SQLite there's a small inbuilt timeout in
an attempt to serialise transactions but it is possible to
exceed it easily and recovery is usually infeasible.
Instead we use the serial_sessions
option to obtain a
mutex before allocating a session.
Method ORM.arranged_session(self, *a, **kw)
Arrange a new session for this Thread
.
Method ORM.declare_schema(self)
Declare the database schema / ORM mapping.
This just defines the relation types etc.
It does not act on the database itself.
It is called automatically at the end of __init__
.
Example:
def declare_schema(self):
""" Define the database schema / ORM mapping.
"""
orm = self
Base = self.Base
class Entities(
........
self.entities = Entities
After this, methods can access the example Entities
relation
as self.entites
.
Property ORM.default_session
The current per-Thread
session.
Property ORM.engine
SQLAlchemy engine, made on demand.
Method ORM.shutdown(self)
Stub shutdown.
Method ORM.startup(self)
Startup: define the tables if not present.
Function orm_auto_session(method)
Decorator to run a method in a session derived from self.orm
if a session is not presupplied.
Intended to assist classes with a .orm
attribute.
See with_session
for details.
Function set_json_field(column_value, field_name, value, *, infill=False)
Set a new value
for field_name
of column_value
.
Return the new column_value
.
Parameters:
column_value
: the original value of the columnfield_name
: the field within the column to locatevalue
: the value to store asfield_name
infill
: optional keyword parameter, defaultFalse
. If true,column_value
and its innards will be filled in asdict
s to allow deferencing thefield_name
.
As with find_json_field
,
a true infill
may modify column_value
to provide field_name
which is why this function returns the new column_value
.
Examples:
>>> set_json_field({'a': 2}, 'a', 3)
{'a': 3}
>>> set_json_field({'a': 2, 'b': {'c': 5}}, 'b.c', 4)
{'a': 2, 'b': {'c': 4}}
>>> set_json_field({'a': 2}, 'b.c', 4)
Traceback (most recent call last):
...
KeyError: 'b'
>>> set_json_field({'a': 2}, 'b.c', 4, infill=True)
{'a': 2, 'b': {'c': 4}}
>>> set_json_field(None, 'b.c', 4, infill=True)
{'b': {'c': 4}}
Class SQLAState(cs.threads.State,_thread._local)
Thread local state for SQLAlchemy ORM and session.
Method SQLAState.auto_session(self, *, orm=None)
Context manager to use the current session
if not None
, otherwise to make one using orm
or self.orm
.
Method SQLAState.new_session(self, *, orm=None)
Context manager to create a new session from orm
or self.orm
.
Function using_session(orm=None, session=None)
A context manager to prepare an SQLAlchemy session for use by a suite.
Parameters:
orm
: optional reference ORM, an object with a.session()
method for creating a new session. Default: if needed, obtained from the globalstate.orm
.session
: optional existing session. Default: the globalstate.session
if notNone
, otherwise created byorm.session()
.
If a new session is created, the new session and reference ORM
are pushed onto the globals state.session
and state.orm
respectively.
If an existing session is reused,
the suite runs within a savepoint from session.begin_nested()
.
Function with_orm(function, *a, orm=None, **kw)
Call function
with the supplied orm
in the shared state.
Function with_session(function, *a, orm=None, session=None, **kw)
Call function(*a,session=session,**kw)
, creating a session if required.
The function function
runs within a transaction,
nested if the session already exists.
If a new session is created
it is set as the default session in the shared state.
This is the inner mechanism of @auto_session
and
ORM.auto_session
.
Parameters:
function
: the function to calla
: the positional parametersorm
: optional ORM class with a.session()
context manager method such as theORM
base class supplied by this module.session
: optional existing ORM sessionkw
: other keyword arguments, passed tofunction
One of orm
or session
must be not None
; if session
is None
then one is made from orm.session()
and used as
a context manager.
The session
is also passed to function
as
the keyword parameter session
to support nested calls.
Release Log
Release 20210420:
- ORM: drop .Session from docstring, no longer used.
- Rename ORM.sessionmaker to ORM._sessionmaker, not for public use.
- ORM: replace session with arranged_session, which allocates a session in conformance with ORM.serial_sessions (serial sessions are used with SQLite).
- Drop @ORM.auto_session and @ORM.orm_method decorators, no longer used.
- SQLAState.new_session: use orm.arranged_session(), use begin_nested(); SQLAState.auto_session: use begin_nested().
Release 20210322: Delete escaped debug code which issued a RuntimeError.
Release 20210321:
- Default session support, particularly though an ORM's .sqla_state per-Thread state object - this allows removal of a lot of plumbing and @auto_session decoration.
- Support for serialised sessions, for db backend where only one session may be active at a time; this brings easy support for multithreaded SQLite access.
Release 20210306:
- Rename _state to state, making it public.
- Some other internal changes.
Release 20201025:
- New BasicTableMixin and HasIdMixin classes with useful methods and a typical
id
Column respectively. - Assorted fixes and improvements.
Release 20190830.1: Have the decorators set .module.
Release 20190830: @json_column: small docstring improvement.
Release 20190829:
- Bugfix @json_column setter: mark the column as modified for the ORM.
- New push_log_level context manager and @log_level decorator to temporarily change the SQLAlchemy logging handler level.
Release 20190812:
- Make ORM a MultiOpenMixin.
- get_json_field: use forgotten
default
parameter. - Other minor changes.
Release 20190526:
- Support for virtual columns mapped to a JSON column interior value:
- New functions find_json_field, get_json_field, set_json_field.
- New decorator @json_column for declaritive_base tables.
Release 20190517:
- Make ORM._Session private session factory the public ORM.Session factory for external use.
- with_session: preexisting sessions still trigger a session.begin_nested, removes flush/commit tension elsewhere.
Release 20190403:
- Rename @ORM.orm_auto_session to @ORM.auto_session.
- New @orm_auto_session decorator for methods of objects with a .orm attribute.
Release 20190319.1: Initial release. ORM base class, @auto_session decorator.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Hashes for cs.sqlalchemy_utils-20210420.tar.gz
Algorithm | Hash digest | |
---|---|---|
SHA256 | 45d96d6cc84e40ecc80eaa041bfd69ac2383b59cf234dfe20559b9c03d0a30e2 |
|
MD5 | 00dba7269bd3fb1ee750c5bf6a829e61 |
|
BLAKE2b-256 | cfc766840d152db5fe0312468099d20e3b7fed916a7737fdacdc8c531fa85094 |