Mapping any class
Under the hood, models subclassing Model and other classes used as model are mapped using a Mapper.
A mapper contains all the information needed to hydrate/dehydrate an object as well as utility methods
to create SQL statements to fetch/save the data.
Defining mappers
Create a mapper and map some columns:
from sqlorm.mapper import Mapper, Column
class Task:
pass
mapper = Mapper(Task, "tasks")
mapper.map({
"id": Column("id", primary_key=True),
"title": Column("title"),
"done": Column("completed") # in this example, the column is named "completed" but the attribute is named "done"
})
Note
The Column class used here is not the same as the one used in the models section.
Column used in the model definitions is a subclass of the mapper's Column with support for property accessors and dirty attributes.
By default, unknown columns will be accepted and set as attributes. This can be changed using allow_unknown_columns=False in mapper's constructor.
Mappers can be created from any classes, in which case annotations will be used:
from sqlorm import PrimaryKey
class Task:
id: PrimaryKey[int]
title: str
done: bool # no column alias possible in this way (but possible when subclassing Model)
mapper = Mapper.from_class(Task)
Tip
Get the mapper associated to models using Mapper.from_class(ModelClass) or ModelClass.__mapper__
Hydrate objects
Use row data to populate an object.
hydrate_new() is used to create new object and hydrate it, hydrate() for existing objects.
data = {"id": 1, "title": "task", "completed": True}
task = mapper.hydrate_new(data)
task = Task()
mapper.hydrate(task, data) # hydrate an existing object
Important
When hydrating objects, __init__() will be bypassed as well as any custom __setattr__.
By default, unmapped columns will be used and set as attributes. To prevent this, use allow_unknown_columns=False in the Mapper constructor or
with_unknown=False in hydrate methods.
Attributes that have been hydrated are available under the object's __hydrated_attrs__ attribute.
Dehydrating objects
Extract data from an object and construct row data.
task = Task()
task.id = 1
task.title = "task"
task.done = True
data = mapper.dehydrate(task)
By default, if unknown columns are allowed, dehydration will also take into account all unknown attributes from __hydrated_attrs__.
Use dehydrate(..., with_unknown=False) to prevent this.
When creating update statements, use with_primary_key=False to not include the primary key as part of the dehydration process.
Generating SQL statements
A few useful methods to generate statementes:
select_from()to generate a select statement with the columns and table part already filled. The column list is generated using the mapping information and falls back to "*" when no columns are provided.select_by_pk()to generate a select statement with a where condition on the primary keyinsert()to generate the insert statement for an objectupdate()to generate the update statementdelete()to generate the delete statement
with engine as tx:
tasks = tx.fetchhydrated(mapper, mapper.select_from())
todos = tx.fetchhydrated(mapper, mapper.select_from().where("not done"))
task1 = tx.fetchhydrated(mapper, mapper.select_from().where("id = 1")).first()
task1 = tx.fetchhydrated(mapper, mapper.select_by_pk(1)).first()
task = Task()
tx.execute(mapper.insert(task))
task = Task()
task.id = 1
task.title = "task"
tx.execute(mapper.update(task))
tx.execute(mapper.delete(task))
The mapper column list mapper.columns is an SQL.Cols object which means it can be used to generate sql conditions:
with engine as tx:
todos = tx.fetchhydrated(mapper, mapper.select_from().where(mapper.columns.done==False))