Accessing the table¶
Asynchronously access the created table using convinient sl3aio interfaces.
Introduction¶
When you have obtained the table, you can access it. sl3aio library provides CRUD (Create, Read, Update, Delete) and several other operations on tables.
Important
Before accessing the table, you must first run the table’s executor. You can do it with async context
manager or with Table.start_executor() and Table.stop_executor() methods:
# With async context manager
async with table:
# Your code goes here
# Or with start_executor and stop_executor methods
table.start_executor()
# Your code goes here
table.stop_executor()
Tip
Instead of manually starting and stopping the table’sexecutor, you can wrap the table in an
EasyTable class that automatically manages the executor when performing common operations.
from sl3aio import EasyTable
wrapped_table = EasyTable(table)
await wrapped_table.<operation>
# The same as:
async with table:
await table.<operation>
Tip
If you’ve created table’s markup using EasyTable and EasyColumn classes you’d better instantiate the EasyTable via the markup class constructor:
class UsersTableMarkup(EasyTable[str | int]):
id: EasySelector[int] = EasyColumn(default=0, primary=True, nullable=False)
name: EasySelector[str] = EasyColumn(nullable=False)
email: EasySelector[str] = ''
age: EasySelector[int]
table = Table('my_table', UsersTableMarkup.columns())
users_table = UsersTableMarkup(table)
Inserting records¶
To insert a new record in the table, you can use one of the Table.insert() or
Table.insert_many() methods.
Hint
Almost every operation on the table, that modifies the records list in it, returns/yields the affected
records. In sl3aio, records are represented by the TableRecord class. This is a subtype of a
tuple that provides access to values not only by index, but also by column name through the getattr and
getitem methods.
So if you have a record TableRecord(id=1, name="Alice", email="Alice@example.com", age=20), you can
access its values like this:
print(record.id) # Output: 1
print(record['name']) # Output: Alice
print(record[-1]) # Output: 20
You also can convert it to dictionary using TableRecord.asdict() method and to tuple using
TableRecord.astuple() method:
print(record.asdict()) # Output: {'id': 1, 'name': 'Alice', 'email': 'Alice@example.com', 'age': 2}
print(record.astuple()) # Output: (1, 'Alice', 'Alice@example.com', 20)
Single at once¶
The Table.insert() method is used to insert a single record in the table. Returns inserted record.
Parameters:
ignore_existing: If set to true, the existing record will be updated, optional, default is True.**values: Values of the record’s columns, given as a keyword arguments. If the value for some column(-s) is not specified, the column’s default value will be passed instead.
Example:
inserted_record = await table.insert(id=1, name="Alice", email="Alice@example.com", age=20)
Multiple at once¶
The Table.insert_many() method is used to insert multiple records in the table at once. Returns the
asynchronous iterator, yielding the inserted records.
Important
You must iterate other the resulted iterator, otherwise the insertion won’t be performed.
Parameters:
ignore_existing: If set to true, the existing record will be updated, optional, default is True.*values: Dictionaries, where each dictionary represents a record with column names as keys and values as values. If the value for some column(-s) is not specified, the column’s default value will be passed instead.
Example:
async for inserted_record in table.insert_many(
{'id': 2, 'name': 'Bob', 'email': 'Bob@example.dev', 'age': 26},
{'id': 3, 'name': 'Charlie', 'email': 'Charlie@example.dev', 'age': 37}
):
pass
Filtering records¶
sl3aio uses predicates to determine which records should be selected/modified and which should be ignored during operations. Predicate is an async function that takes the record as a parameter, and returns whether the record should be selected/modified or not. There is currentrly two ways to create predicates.
Via EasySelector¶
The EasySelector class allows you to create complex selection criteria in pythonic way via operator
overloading. At a start point, EasySelector has the record as the underlying object. Then you can
use operators to control the selection.
Note
The EasySelector class just like the EasyTable automatically manages the executor
when performing common operations on the pinned table.
You can pin the table to a selector using the EasySelector.pin_table() method or pass the table
to the constructor of the EasySelector class.
First create an instance:
from sl3aio import EasySelector
selector = EasySelector[str | int]()
Hint
- The
EasySelectorclass constructor takes the following parameters: table: The pinned table, optional, defaults to None.selector: The initial selector, optional, defaults tolambda record: record, True.
- The
You can specify the data types of the table inside the
EasySelectorgeneric.
Now you can create a selector.
selector.<attribute name>
selector[<item name or index or slice>]
# These operators are responsible for the result of predicates.
# If logical operator returns false,
# the record will not be selected/modified.
selector (== or != or < or > or <= or >=) <value>
selector.(is_ or is_not_ or in_ or or_ or and_)(<value>)
selector.not_()
<value> in selector
.. selector.set_ok(True or False) # Ensure that the selector is succeeded/failed
selector (+ or - or * or / or ** or % or // or @) <value>
(- or + or ~)selector
(abs or round or ceil or floor or trunc or int or float or complex)(selector)
selector (<< or >> or ^ or & or |) <value>
selector(*args, **kwargs)
# The key_or_pos parameter specifies where the current selector's
# object will be passed to the function.
selector.pass_into(func, key_or_pos=..., *other_args, **other_kwargs)
Note
You can compare selectors with each other, in other words replace <value> in examples with other
selector.
After you has finished setting up the selector, you can either perform common operations (select, update, delete) on it, as it was a table, or you can convert it into a predicate:
predicate = selector.as_predicate()
To check a record against the selector, you can use the EasySelector.apply() method which returns a
tuple, containing a boolean indicating if the selector matched, and the result of the selector application:
ok, result = selector.apply(record)
Via callable¶
You can create a predicate via a callable. The callable should be asynchronous, take a record as a parameter and return a boolean indicating if the record should be selected/modified.
async def my_predicate(record: TableRecord) -> bool:
# Your implementation here
Selecting records¶
To select records from the table, you can use one of these methods:
Table.select()(predicate=None): Yields all of the records that matched the given predicate. If predicate isn’t specified, yields the entire table. You need to iterate over the result for the operation to be performed.Table.select_one()(predicate=None): Returns the first yielded byselectrecord or None if no records was selected. If predicate isn’t specified, returns the first record in the table.
From Table or EasyTable¶
async with table:
async for record in table.select(predicate):
# Your implementation here
selected_record = await table.select_one(predicate)
async for record in table.select(predicate):
# Your implementation here
selected_record = await table.select_one(predicate)
From EasySelector¶
Since the EasySelector is the same thing as a predicate, you don’t need to pass predicate inside
its EasySelector.select() and EasySelector.select_one() methods.
Note
If you don’t have a table pinned to EasySelector, you need to pass your table instead of the
predicate argument.
async for record in selector.select():
# Your implementation here
selected_record = await selector.select_one()
Updating records¶
To update records in the table, you can use one of these methods:
Table.updated()(predicate=None, **to_update): Updates values specified in the**to_updateparameter for each record that matched the given predicate and yields the updated records. If predicate isn’t specified, yields and updates every record in the table. You need to iterate over the result for operation to be performed.Table.update()(predicate=None, **to_update): Updates values specified in the**to_updateparameter for each record that matched the given predicate without yielding the updated records. If predicate isn’t specified, updates every record in the table.Table.update_one()(predicate=None, **to_update): Updates values specified in the**to_updateparameter for the first record that matched the given predicate and returns the updated record or None if no record was updated. If predicate isn’t specified, updates the first record in the table and returns it.
From Table or EasyTable¶
async with table:
async for record in table.updated(predicate, **to_update):
# Your implementation here
await table.update(predicate, **to_update)
updated_record = await table.update_one(predicate, **to_update)
async for record in table.updated(predicate, **to_update):
# Your implementation here
await table.update(predicate, **to_update)
updated_record = await table.update_one(predicate, **to_update)
From EasySelector¶
Since the EasySelector is the same thing as a predicate, you don’t need to pass predicate inside
its EasySelector.updated(), EasySelector.update() and EasySelector.update_one()
methods.
Note
If you don’t have a table pinned to EasySelector, you need to pass your table instead of the
predicate argument.
async for record in selector.updated(**to_update):
# Your implementation here
await selector.update(**to_update)
updated_record = await selector.update_one(**to_update)
Deleting records¶
To delete records in the table, you can use one of these methods:
Table.deleted()(predicate=None): Deletes and yields removed records that matched the given predicate. If predicate isn’t specified, yields and clears the table. You need to iterate over the result for operation to be performed.Table.delete()(predicate=None): Deletes records that matched the given predicate without yielding removed ones. If predicate isn’t specified, clears the table.Table.delete_one()(predicate=None): Deletes and returns the first record that matched the given predicate. If predicate isn’t specified, deletes and returns the first record in the table.
From Table or EasyTable¶
async with table:
async for record in table.deleted(predicate):
# Your implementation here
await table.delete(predicate)
removed_record = await table.delete_one(predicate)
async for record in table.deleted(predicate):
# Your implementation here
await table.delete(predicate)
removed_record = await table.delete_one(predicate)
From EasySelector¶
Since the EasySelector is the same thing as a predicate, you don’t need to pass predicate inside
its EasySelector.deleted(), EasySelector.delete() and EasySelector.delete_one()
methods.
Note
If you don’t have a table pinned to EasySelector, you need to pass your table instead of the
predicate argument.
async for record in selector.deleted():
# Your implementation here
await selector.delete()
removed_record = await selector.delete_one()
Other operations¶
There are several other operations that is currently supported by the sl3aio. Some of them are common (e.g. must be implemented by every type of table) and the others are not.
Common¶
This operations must be implemented by every type of table.
Length¶
The Table.length() method returns the amount of records in the table.
length = await table.length()
Count¶
The Table.count() (predicate=None) method returns the amount of records in the table that matches the
given predicate.
Note
If you won’t specify the predicate, the result will be the same as the length of the table.
count = await table.count(predicate)
Contains¶
The Table.contains() (record) method returns True if the table contains the given record.
contains = await table.contains(record)
SqlTable operations¶
This operations are supported only by the subclasses of the SqlTable (e.g.
SolidTable).
Exists¶
The SqlTable.exists() method checks if the table exists in the database.
exists = await table.exists()
Create¶
The SqlTable.create() (if_not_exists=True) method creates the table in the database.
await table.create()
Tip
You can optionally set the if_not_exists parameter to False to remove IF NOT EXISTS clause from the
creation query.
Drop¶
The SqlTable.drop() (if_exists=True) method drops the table from the database.
await table.drop()
Tip
You can optionally set the if_exists parameter to False to remove IF EXISTS clause from the
deletion query.