class User: name:str; pwd:str
class Todo: title:str; name:str; id:int; done:bool=False; details:str=''
class Student: id:int; grad_year:int; name:str
Source
Database
and DBTable
We create a Database
class and a DBTable
class (which is returned by Database.create
), using sqlalchemy v2. These classes will allow us to directly work with dataclasses such as these:
Database
Database (conn_str)
A connection to a SQLAlchemy database
= Database("sqlite:///:memory:")
db # db = Database("postgresql://")
DBTable
DBTable (table:sqlalchemy.sql.schema.Table, database:__main__.Database, cls)
A connection to a SQLAlchemy table, created if needed
Database.create
Database.create (cls:type, pk='id', name:str|None=None)
Get a table object, creating in DB if needed
= db.create(User, pk='name')
users = db.create(Todo, pk='id')
todos = db.create(Student, pk=('id', 'grad_year')) students
Database.schema
Database.schema ()
Show all tables and columns
print(db.schema())
Table: student
* id: INTEGER
* grad_year: INTEGER
- name: VARCHAR
Table: todo
- title: VARCHAR
- name: VARCHAR
* id: INTEGER
- done: BOOLEAN
- details: VARCHAR
Table: user
* name: VARCHAR
- pwd: VARCHAR
DBTable.exists
DBTable.exists ()
Check if this table exists in the DB
users.exists()
True
= User('jph','foo')
u0 = User('rlt','bar')
u1 = Todo('do it', 'jph')
t0 = Todo('get it done', 'rlt') t1
DBTable.insert
DBTable.insert (obj)
Insert an object into this table, and return it
= todos.insert(t0)
t assert t.id
t
Todo(title='do it', name='jph', id=1, done=False, details='')
= users.insert(u0)
u assert u.name=='jph'
users.insert(u1) u
User(name='jph', pwd='foo')
todos.insert(t1)
Todo(title='get it done', name='rlt', id=2, done=False, details='')
DBTable.__call__
DBTable.__call__ (where:str|None=None, where_args:Union[Iterable,dict,NoneType]=None, order_by:str|None=None, limit:int|None=None, offset:int|None=None, select:str='*', **kw)
Result of select
query on the table
Type | Default | Details | |
---|---|---|---|
where | str | None | None | SQL where fragment to use, for example id > ? |
where_args | Union | None | Parameters to use with where ; iterable for id>? , or dict for id>:id |
order_by | str | None | None | Column or fragment of SQL to order by |
limit | int | None | None | Number of rows to limit to |
offset | int | None | None | SQL offset |
select | str | * | Comma-separated list of columns to select |
kw | |||
Returns | list | List of returned objects |
assert users()==[u0,u1]
users()
[User(name='jph', pwd='foo'), User(name='rlt', pwd='bar')]
= users(where="pwd LIKE :pwd", pwd="b%")
r assert r==[u1]
r
[User(name='rlt', pwd='bar')]
='rlt')
users.xtra(name='name') users(order_by
[User(name='rlt', pwd='bar')]
='name="rlt"') users(where
[User(name='rlt', pwd='bar')]
='name="jph"') users(where
[]
assert len(todos())==2
todos()
[Todo(title='do it', name='jph', id=1, done=False, details=''),
Todo(title='get it done', name='rlt', id=2, done=False, details='')]
NotFoundError
Common base class for all non-exit exceptions.
DBTable.__getitem__
DBTable.__getitem__ (key)
Get item with PK key
='jph')
users.xtra(name
assert users['jph']==u0
'jph'] users[
User(name='jph', pwd='foo')
='rlt')
users.xtra(namelambda: users['jph']==u0) test_fail(
DBTable.update
DBTable.update (obj=None, **kw)
='jph')
users.xtra(name= 'new'
u.pwd
users.update(u)
users.xtra() users()
[User(name='jph', pwd='new'), User(name='rlt', pwd='bar')]
='rlt')
users.xtra(name= 'foo'
u.pwd
users.update(u)
users.xtra()'jph'].pwd, 'new') test_eq(users[
DBTable.delete
DBTable.delete (key)
Delete item with PK key
and return count deleted
assert users.delete('jph')
lambda: users['jph']) test_fail(
DBTable.__contains__
DBTable.__contains__ (pk_values:Union[list,tuple,str,int])
Is the item with the specified primary key value in this table?
Type | Details | |
---|---|---|
pk_values | Union | A single value, or a tuple of values for tables that have a compound primary key |
Returns | bool |
Demonstration with single field primary key:
assert not 'jph' in users
assert 'rlt' in users
For compound primary keys, lets whether a student is in the students table or not.
1, 2021, 'jph')) students.insert(Student(
Student(id=1, grad_year=2021, name='jph')
assert (1,2021) in students
assert (1,2030) not in students
SQLAlchemy helpers
ReadOnlyColumnCollection.__dir__
ReadOnlyColumnCollection.__dir__ ()
Default dir() implementation.
MetaData.__dir__
MetaData.__dir__ ()
Default dir() implementation.
= db.meta dbm
' '.join(dbm.tables)
'user todo student'
= dbm.todo t
list(t.c)
[Column('title', String(), table=<todo>),
Column('name', String(), table=<todo>),
Column('id', Integer(), table=<todo>, primary_key=True, nullable=False),
Column('done', Boolean(), table=<todo>),
Column('details', String(), table=<todo>)]
from sqlalchemy.exc import ResourceClosedError
MetaData.sql
MetaData.sql (statement, *args, **kwargs)
Execute statement
string and return DataFrame
of results (if any)
Connection.sql
Connection.sql (statement, nm='Row', *args, **kwargs)
Execute statement
string and return results (if any)
CursorResult.tuples
CursorResult.tuples (nm='Row')
Get all results as named tuples
# dbm.sql('delete from todo')
# db.conn.commit()
= dbm.sql('select * from user')
rs 0] rs[
Row(name='rlt', pwd='foo')
Table.get
Table.get (where=None, limit=None)
Select from table, optionally limited by where
and limit
clauses
'd'), limit=5) t.get(t.c.title.startswith(
[Row(title='do it', name='jph', id=1, done=False, details='')]
This is the query that will run behind the scenes:
print(t.select().where(t.c.title.startswith('d')).limit(5))
SELECT todo.title, todo.name, todo.id, todo.done, todo.details
FROM todo
WHERE (todo.title LIKE :title_1 || '%')
LIMIT :param_1
MetaData.close
MetaData.close ()
Close the connection
dbm.close()