API reference¶
Connecting to a database¶
- wurm.setup_connection(conn)¶
Call this once in each OS thread with a
sqlite3.Connection
, before accessing the database via wurm.This records the connection and ensures all tables are created.
Defining tables¶
- class wurm.tables.BaseTable(*args, **kwargs)¶
Baseclass for your own tables. Tables must be dataclasses.
Do not use directly, subclass
wurm.Table
orwurm.WithoutRowid
instead.Use the keyword argument name in the class definition to set the table name:
@dataclass class MyTable(Table, name='mytable'): ...
If not given, wurm uses the class name to automatically derive a suitable table name.
Use the keyword argument abstract in the class definition to add fields or methods that you want to share between several tables:
@dataclass class HasOwner(Table, abstract=True): owner: str def display_owner(self): return self.owner.capitalize()
The above will not create a new table, but subclasses of
HasOwner
will have a field calledowner
and a method calleddisplay_owner
.Models are context managers, so one could write:
with MyTable.query(field=value) as obj: obj.field = other_value
This automatically calls
commit()
onobj
as long as no exception is raised inside thewith
-block, in which caseobj
is left in a dirty state.- classmethod query(**kwargs)¶
Create a query object.
The names of keywords passed should be rowid or any of the fields defined on the table.
The values can either be Python values matching the types of the relevant fields, or the same wrapped in one of
lt()
,gt()
,le()
,ge()
,eq()
orne()
. When unwrapped, the behavior matches that of values wrapped ineq()
.Merely creating a query does not access the database.
- Returns
A query for this table.
- Return type
- classmethod __len__()¶
The total number of rows in this table. A shortcut for
len(table.query())
.Note
This method accesses the connected database.
- classmethod __iter__()¶
Iterate over all the objects in the table. A shortcut for
iter(table.query())
Note
This method accesses the connected database.
- commit()¶
Commits any changes to the object to the database.
Note
This method accesses the connected database.
- delete()¶
Deletes this object from the database.
Note
This method accesses the connected database.
- Raises
ValueError – if called twice on the same instance, or called on a fresh instance that has not been inserted yet.
- insert()¶
Insert a new object into the database.
Note
This method accesses the connected database.
- class wurm.Table¶
Baseclass for regular rowid tables. See
BaseTable
for methods available on subclasses.
- class wurm.WithoutRowid¶
Baseclass for
WITHOUT ROWID
tables. You need to add an explicit primary key usingPrimary
for these kinds of tables. SeeBaseTable
for methods available on subclasses.
Annotations¶
Tables are defined using PEP 526 type annotations, where the type for each column has to be one of the following:
One of the basic supported types (currently
str
,bytes
,int
,float
,bool
,datetime.date
,datetime.time
,datetime.datetime
andpathlib.Path
).A type registered with
wurm.register_type()
.A previously defined
wurm.Table
orwurm.WithoutRowid
subclass.wurm.Primary[T]
,wurm.Index[T]
orwurm.Unique[T]
, whereT
is one of the types mentioned above.
- wurm.Primary¶
Using
Primary[T]
as a type annotation in a table definition is equivalent to usingT
, except that the column will be part of the primary key. If multiple fields on a single table definition are annotated in this way, their columns form a composite primary key together.If you attempt change the database in a way that would cause two rows to share a primary key, the operation is rolled back, and a
WurmError
is raised.
- wurm.Index¶
Using
Index[T]
as a type annotation in a table definition is equivalent to usingT
, except that a (non-UNIQUE
) index is created for the field.
- wurm.Unique¶
Using
Unique[T]
as a type annotation in a table definition is equivalent to usingT
, except that aUNIQUE
index is created for the field. Note that SQL considersNone
values to be different from otherNone
values for this purpose.If you attempt to call
insert()
orcommit()
in a way that would violate such a constraint, the operation is rolled back, and aWurmError
is raised.
- wurm.register_type(python_type, sql_type, *, encode, decode)¶
Registers a type for use in model fields.
For example:
class Foo: def __repr__(self): ... @classmethod def from_string(cls, string): ... ... register_type(Foo, str, encode=repr, decode=Foo.from_string)
- Parameters
python_type (type) – The type to register
sql_type – The stored type, one of
int
,str
,float
,bytes
, a dict fromstr
to the above primitive types, or a tuple of the above primitive types. The latter two options is for types that should be mapped to multiple columns.encode (python_type -> sql_type) – The function to prepare to store a value in the database. Should return a tuple if the type is mapped to multiple columns.
decode (sql_type -> python_type) – The function to interpret the stored value. Should take multiple arguments in the case types mapped to multiple columns.
- wurm.register_dataclass(dclass)¶
Registers a dataclass for use in model fields.
This is a convenience function that can optionally be used as a decorator. Given:
@dataclasses.dataclass class Color: r: float g: float b: float
then the following:
register_dataclass(Color)
is equivalent to:
register_type(Color, dict(r=float, g=float, b=float), encode=dataclasses.astuple, decode=Color)
In either case, the model:
class MyTable(Table): color: Color
will have the fields
color_r
,color_g
andcolor_b
, which will transparently be converted to and fromColor
objects.- Parameters
dclass – The dataclass to register
- Returns
The registered dataclass
Queries¶
Query objects¶
Most advanced queries will be done through Query
objects, that
can be created either explicitly through their constructor, or by
calling Table.query()
.
- class wurm.Query(table: Type[wurm.queries.T], filters: Dict[str, Any])¶
Represents one or more queries on a specified table.
Query(table, filters)
is equivalent totable.query(**filters)
- __iter__() → Iterator[wurm.queries.T]¶
Iterate over the results of this query.
Note
This method accesses the connected database.
Equivalent to
select_with_limit()
without specifying limit.
- __len__() → int¶
Returns the number of rows matching this query.
Note
This method accesses the connected database.
- Returns
number of matches
- Return type
int
- delete() → None¶
Delete the objects matching this query.
Warning
Calling this on an empty query deletes all rows of the relevant table in the database
Note
This method accesses the connected database.
- Returns
the number of rows deleted
- Return type
int
- first() → wurm.queries.T¶
Return the first result of this query.
Note
This method accesses the connected database.
- Raises
WurmError – if this query returns zero results
- one() → wurm.queries.T¶
Return the only result of this query.
Note
This method accesses the connected database.
- Raises
WurmError – if this query returns zero results or more than one
- select_with_limit(limit: Optional[int] = None) → Iterator[wurm.queries.T]¶
Create an iterator over the results of this query.
Note
This method accesses the connected database.
- Parameters
limit (int or None) – The number of results to limit this query to.
- Returns
an iterator over the objects matching this query.
Comparators¶
- wurm.lt(value)¶
- wurm.le(value)¶
- wurm.eq(value)¶
- wurm.ne(value)¶
- wurm.ge(value)¶
- wurm.gt(value)¶
Used to wrap values in queries. These functions correspond to the special names for the Python comparison operators.
The expression
MyTable.query(a=le(1), b=gt(2), c=3, d=ne(4))
is roughly equivalent to
SELECT * FROM MyTable WHERE a <= 1 AND b > 2 AND c = 3 AND d != 4
Replacing
c=3
withc=eq(3)
is optional.