Welcome to Datalite’s documentation!¶
Datalite is a simple to use Python library that can bind a dataclass to an sqlite3 database.
Documentation¶
Getting Started¶
Welcome to the documentation of datalite. Datalite provides a simple, intuitive way to bind dataclasses
to sqlite3 databases. In its current version, it provides implicit support for conversion between
int
, float
, str
, bytes
classes and their sqlite3
counterparts, default values,
basic schema migration and fetching functions.
Installation¶
Simply write:
pip install datalite
In the shell. And then, whenever you want to use it in Python, you can use:
import datalite
Basic Decorator Operations¶
Creating a datalite class¶
A datalite class is a special dataclass. It is created by using a decorator @datalite.datalite
,
members of this class are, from Python’s perspective, just normal classes. However, they have
additional methods and attributes. @datalite
decorator needs a database path to be provided.
This database is the database the table for the dataclass will be created.
from datalite import datalite
@datalite(db_path='db.db')
@dataclass
class Student:
student_id: int = 1
student_name: str = "Kurt Gödel"
student_gpa: float = 3.9
Here, datalite
will create a table called student
in the database file db.db
, this
file will include all the fields of the dataclass as columns. Default value of these columns
are same as the default value of the dataclass.
Special Methods¶
Each object initialised from a dataclass decorated with the @dataclass
decorator automatically
gains access to three special methods. It should be noted, due to the nature of the library, extensions
such as mypy
and IDEs such as PyCharm will not be able to see these methods and may raise exceptions.
With this in mind, let us create a new object and run the methods over this objects.
new_student = Student(0, "Albert Einstein", 4.0)
Creating an Entry¶
First special method is .create_entry()
when called on an object of a class decorated with the
@datalite
decorator, this method creates an entry in the table of the bound database of the class,
in this case, table named student
in the db.db
. Therefore, to create the entry of Albert Einstein
in the table:
new_student.create_entry()
This also modifies the object in an intresting way, it adds a new attribute obj_id
, this is a unique,
autoincremented value in the database. It can be accessed by new_student.obj_id
.
Updating an Entry¶
Second special method is .update_entry()
. If an object’s attribute is changed, to update its
record in the database, this method must be called.
new_student.student_gpa = 5.0 # He is Einstein, after all.
new_student.update_entry()
Deleting an Entry¶
To delete an entry from the record, the third and last special method, .remove_entry()
should
be used.
new_student.remove_entry()
Warning
It should be noted that, if the new_student.obj_id
attribute is modified, .update_entry()
and .remove_entry()
may have unexpected results.
Constraints¶
One of the most useful features provided by SQLite is the concept of constraints. Constraints signal the SQL engine that the values hold in a specific column MUST abide by specific constraints, these might be
Values of this column cannot be
NULL
. (NOT NULL
)Values of this column cannot be repeated. (
UNIQUE
)Values of this column must fulfill a condition. (
CHECK
)Values of this column can be used to identify a record. (
PRIMARY
)Values of this column has a default value. (
DEFAULT
)
Some of these constraints are already implemented in datalite. With all of the set, is planned to be implemented in the future.
Default Values¶
Columns can be given default values. This is done the same way you would give a datafield a default value.
@datalite("db.db")
@dataclass
class Student:
id_: int
name: str = "Albert Einstein"
Therefore, from now on, any Student
object, whose name is not specified, will
have the default name "Albert Einstein"
and if .create_entry()
method is
called on them, the newly inserted record will, by default, have this value in its
corresponding column.
Unique Values¶
Declaring a field unique is done by a special TypeVar
called Unique
this uniqueness check is done in the database level, this introduces has some pros,
but also some cons.
Pushing the uniqueness check to the database level introduces a better ability to
handle concurrency for applications with large traffic, however, this check only
runs when an item is registered, which means no problem will raise in
the object creation even if another object of the same type with the same value
hold in the unique field exists, no error will raise. However, if another record
with the same value in the unique field is recorded in the bound database, upon
the invocation of the .create_entry()
will raise the ConstraintFailedError
exception.
Uniqueness constraint is declared thusly:
from datalite.constraints import Unique
@datalite("db.db")
@dataclass
class Student:
id_: Unique[int]
name: str = "Albert Einstein"
Hinting a field with the Unique[T]
type variable will introduce two rules:
The values in the column of the field in the table that represents the dataclass
Student
in the bound databasedb.db
cannot be NULL, thus the corresponding field cannot be assignedNone
.These same values must be unique for each and every record.
Failure of any of these two rules will result in a ConstraintFailedError
exception.
Fetching Functions¶
A database is hardly useful if data does not persist between program runs. In datalite
one can use datalite.fetch
module to fetch data back from the database.
There are different sorts of fetching. One can fetch all the objects of a class
using fetch_all(class_)
, or an object with a specific object id using fetch_from(class_, obj_id)
.
There are more functions for plural conditional fetching (fetch_if
, fetch_where
) where
all objects fitting a condition will be returned, as well as singular conditional fetching that returns
the first object that fits a condition (fetch_equals
).
Pagination¶
Pagination is a feature that allows a portion of the results to be returned. Since datalite
is built to work with databases that may include large amounts of data, many systems using large
portions of data also make use of pagination. By building pagination inside the system, we hope to
allow easier usage.
fetch_where
fetch_if
fetch_all
Supports pagination, in general, pagination is controlled via two arguments page
and element_count
,
page
argument specifies which page to be returned and element_count
specifies how many elements
each page has. When page
is set to 0, all results are returned irregardless of the value of the
element_count
.
Important
More information regarding the datalite.fetch
functions can be found in the API reference.
Schema Migrations¶
Datalite provides a module, datalite.migrations
that handles schema migrations. When a class
definition is modified, datalite.migrations.basic_migration
can be called to automatically
transfer records to a table fitting the new definitions.
Let us say we have made changes to the fields of a dataclass called Student
and now,
we want these changes to be made to the database. More specifically, we had a field called
studentt_id
and realised this was a typo, we want it to be named into student_id
,
and we want the values that was previously hold in this column to be persistent despite the
name change. We can achieve this easily by:
datalite.basic_migration(Student, {'studentt_id': 'student_id'})
This will make all the changes, if we had not provided the second argument, the values would be lost.
API Reference¶
datalite Module¶
-
@
datalite.
datalite
(db_path: str, type_overload: Optional[Dict[Optional[type], str]] = None) → Callable¶ Bind a dataclass to a sqlite3 database. This adds new methods to the class, such as create_entry(), remove_entry() and update_entry().
- Parameters
db_path – Path of the database to be binded.
type_overload – Type overload dictionary.
- Returns
The new dataclass.
datalite.constraints module¶
- datalite.constraints module introduces constraint
types that can be used to hint field variables, that can be used to signal datalite decorator constraints in the database.
-
exception
datalite.constraints.
ConstraintFailedError
¶ Bases:
Exception
This exception is raised when a Constraint fails.
datalite.fetch module¶
-
datalite.fetch.
fetch_all
(class_: type, page: int = 0, element_count: int = 10) → tuple¶ Fetchall the records in the bound database.
- Parameters
class – Class of the records.
page – Which page to retrieve, default all. (0 means closed).
element_count – Element count in each page.
- Returns
All the records of type class_ in the bound database as a tuple.
-
datalite.fetch.
fetch_equals
(class_: type, field: str, value: Any) → Any¶ Fetch a class_ type variable from its bound db.
- Parameters
class – Class to fetch.
field – Field to check for, by default, object id.
value – Value of the field to check for.
- Returns
The object whose data is taken from the database.
-
datalite.fetch.
fetch_from
(class_: type, obj_id: int) → Any¶ Fetch a class_ type variable from its bound dv.
- Parameters
class – Class to fetch from.
obj_id – Unique object id of the object.
- Returns
The fetched object.
-
datalite.fetch.
fetch_if
(class_: type, condition: str, page: int = 0, element_count: int = 10) → tuple¶ Fetch all class_ type variables from the bound db, provided they fit the given condition
- Parameters
class – Class type to fetch.
condition – Condition to check for.
page – Which page to retrieve, default all. (0 means closed).
element_count – Element count in each page.
- Returns
A tuple of records that fit the given condition of given type class_.
-
datalite.fetch.
fetch_range
(class_: type, range_: range) → tuple¶ Fetch the records in a given range of object ids.
-
datalite.fetch.
fetch_where
(class_: type, field: str, value: Any, page: int = 0, element_count: int = 10) → tuple¶ Fetch all class_ type variables from the bound db, provided that the field of the records fit the given value.
- Parameters
class – Class of the records.
field – Field to check.
value – Value to check for.
page – Which page to retrieve, default all. (0 means closed).
element_count – Element count in each page.
- Returns
A tuple of the records.
datalite.mass_actions module¶
This module includes functions to insert multiple records to a bound database at one time, with one time open and closing of the database file.
-
exception
datalite.mass_actions.
HeterogeneousCollectionError
¶ Bases:
Exception
- :raiseif the passed collection is not homogeneous.
ie: If a List or Tuple has elements of multiple types.
-
datalite.mass_actions.
copy_many
(objects: Union[List[T], Tuple[T]], db_name: str, protect_memory: bool = True) → None¶ Copy many records to another database, from their original database to new database, do not delete old records.
- Parameters
objects – Objects to copy.
db_name – Name of the new database.
protect_memory – Wheter to protect memory during operation, Setting this to False will quicken the operation, but if the operation is cut short, database file will corrupt.
- Returns
None
-
datalite.mass_actions.
create_many
(objects: Union[List[T], Tuple[T]], protect_memory: bool = True) → None¶ Insert many records corresponding to objects in a tuple or a list.
- Parameters
protect_memory – If False, memory protections are turned off, makes it faster.
objects – A tuple or a list of objects decorated with datalite.
- Returns
None.
datalite.migrations module¶
Migrations module deals with migrating data when the object definitions change. This functions deal with Schema Migrations.
-
datalite.migrations.
basic_migrate
(class_: type, column_transfer: dict = None) → None¶ Given a class, compare its previous table, delete the fields that no longer exist, create new columns for new fields. If the column_flow parameter is given, migrate elements from previous column to the new ones. It should be noted that, the obj_ids do not persist.
- Parameters
class – Datalite class to migrate.
column_transfer – A dictionary showing which columns will be copied to new ones.
- Returns
None.